2018年5月10日 星期四

【Android Line Login API】Eclipse上使用Line Login API時碰到Activit not found的解決辦法

呼叫Line的功能會使用到「com.linecorp.linesdk.auth.internal.LineAuthenticationActivity」和「com.linecorp.linesdk.auth.internal.LineAuthenticationCallbackActivity」。

可以在aar檔解壓縮後的Library內找到AndroidManifest.xml檔,並用標準Eclipse的方式匯入。

(Line SDK有提供Maven版。但我一樣沒有用。)


在Library內的AndroidManifest檔下有這兩個設定值,將它們完整複製下來、貼到自己的工作專案下,(光是直接import並不夠,)然後每個「android:exported」都改為true。

接著就可以正常使用了。



        <activity
            android:name="com.linecorp.linesdk.auth.internal.LineAuthenticationActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:exported="false"
            android:launchMode="singleTop"
            android:theme="@style/LineSdk_AuthenticationActivity" />
        <activity
            android:name="com.linecorp.linesdk.auth.internal.LineAuthenticationCallbackActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <data android:scheme="lineauth" />
            </intent-filter>
        </activity>

不要搬移這段設定值,程式依然可以正常執行,但是當進行登入時,系統層的logcat訊息會跳出「Activiy」找不到的警訊然後中斷執行,(在APP層的logcat中看不到。)

還在使用Eclipse的人有興趣可以試試看。


會注意到這點,是因為使用FCM時也碰到類似的情況:必須要把Library中的Manifest檔設定內容搬到APP中的Manifest檔中。

感謝Dandar3寫的說明文件讓我有解決這個問題的基礎。(歡迎大家去Github上拜訪他一下。)

2018年5月9日 星期三

【Android Fragment】Fragment的優缺點

先說缺點。

缺點當然是麻煩!

不單單是設計上,如果要在Fragment中插入Fragment,一定不能走XML,必須要在主Fragment中呼叫getChildFragmentSupport,然後用Java程式碼將子Fragment一個一個插入主Fragment中。

(這很自打嘴巴。因為Google自己當初用非常強勢的方式規劃了一個用XML為主的設計流程思維,現在又告訴大家「這個時候要丟掉這個東西。」)



優點主要有兩個....

一是介面可以大範圍的元件化。如果介面內的資料需要複雜的初始化、但又要可以動態的顯示、隱藏、銷毀(不再使用),過去需要設計複雜的Handler,而且還要處理HandlerLieaking(一種送出的Handler被主執行序完全忽略不執行的狀況),但如果使用Fragment就可以確保「隨時都能插入或移除介面元件」甚至還有「複雜的初始化」。

(FragmentManager可以從任何地方任何時刻向主執行序發出一個修改介面的請求。不管是add/replace/remove一個Fragment,或將一個現存Fragment用簡單的「先detach、再attach」來刷新內容。)


二是延續一,讓設計者可以用介面元件更新控制「流程」或「功能的週期」。一個執行序需要有更明確且簡單的控制判斷時,可以使用Fragment。例如執行序中途需要更新畫面(例如一個TextView),但TextView會有不在畫面上的時刻,如果用Activity層來控制執行序,Handler的設計需求會更複雜,如果改用Fragment,執行序可以直接判斷Fragment的生命週期即可。

(一般的View沒有生命週期,如果要用是否為「null」,就需要獨立製作資料的存取功能,如果用Fragment,則只要用Frgament當存放資料的容器即可,當Fragment需要使用資料時,也只需要再「自己」身上找資料即可。)




2018年4月18日 星期三

【Android】camera2 -- YUV格式圖檔

Android5以後的android.hardware.camera被列為「@Deprecated,請改用android.hardware.camera2。」

但就算不提「android.hardware.camera2」的複雜,傳入的照片資料結構本身跟「android.hardware.camera」的差異也非常大,如果要升級,光是改寫相機硬體與權限套件還不夠,還要連照片的轉檔也一併完成。


Android相機預設的檔案格式稱為YUV,它是種「用三種向量的方式來記錄圖片資料」的格式(完全無法理解怎麼實作),一共有420、422和444三種格式。(但也可以指定相機傳入舊格式檔案,但會造成「無法預覽」,自然也無法進行拍照。這格式的檔案在實用上功能有點不明。)

這檔案的格式技術細節未來需要慢慢補完。



Android使用這種格式的檔案奇妙之處在於:View元件本身幾乎都不支援這個格式的檔案,依然支援傳統RGB(Bitmap)或Drawalbe。

「android.hardware.camera」中取得的是YUV圖片的raw資料格式,也就byte矩陣檔。

但「android.hardware.caemra2」中經由「ImageReader」取得的是YUV圖片的資料格式封裝物件「Image」,這東西並不是YUV檔的實體,也無法直接轉成raw資料格式。

一個Image中有三層「Plane」,每一層都可以視為是對應「YUV」中的一種向量資料,因為是向量,所以每一張照片中的每一層Plane資料格式大小其實都不一定,跟RGB格式不一樣,只要知道的圖片長寬和格式,幾乎就可以順利估算出檔案大小或資料長度。

將Image物件格式轉成raw格式的演算法如下。三層ByteBuffer剛好是依序三層Plane的ByteBuffer資料。

// nv12: true = NV12, false = NV21
    public static byte[] YUV_420_888toNV(ByteBuffer yBuffer, ByteBuffer uBuffer, ByteBuffer vBuffer, boolean nv12) {
        byte[] nv;

        int ySize = yBuffer.remaining();
        int uSize = uBuffer.remaining();
        int vSize = vBuffer.remaining();

        nv = new byte[ySize + uSize + vSize];

        yBuffer.get(nv, 0, ySize);
        if (nv12) {//U and V are swapped
            vBuffer.get(nv, ySize, vSize);
            uBuffer.get(nv, ySize + vSize, uSize);
        } else {
            uBuffer.get(nv, ySize , uSize);
            vBuffer.get(nv, ySize + uSize, vSize);
        }
        return nv;
    }

但用QRCode Decode的套件:ZXing作測試,它可以接受「android.hardware.camera」的raw資料,但仍無法接收這個格式的資料。所以這個演算法還需要再檢討。

但奇妙的是:這個演算法獲得的raw格式資料確實可以轉成RGB格式檔案/Bitmap物件。

演算法如下。


public static byte[] NV21toJPEG(byte[] nv21, int width, int height, int quality) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        YuvImage yuv = new YuvImage(nv21, ImageFormat.NV21, width, height, null);
        yuv.compressToJpeg(new Rect(0, 0, width, height), quality, out);
        return out.toByteArray();
    }

參考連結

https://stackoverflow.com/questions/9325861/converting-yuv-rgbimage-processing-yuv-during-onpreviewframe-in-android


2018年2月12日 星期一

【Android】實際使用Socket傳輸資料

這是在「同一台裝置內的兩個不同APP/線程」要互傳資料時使用Socket的心得。

大致原則跟一般Java的Socket使用並無差異。


1.連線的兩頭都可以使用InetAddress這個物件內的static功能「getLocalHost」來取得IP,不然很有可能會失敗。

2.建議要有一頭使用ServerSocket來當聆聽連線請求端。

3.Socket建立後第一件事是「在迴圈內容重複檢查Connect是否已經建立起來」,因為InputStream/OutputStream本身不會檢查,資料丟進Stream中,即使另一端沒有東西接收也會執行。──建立一個while迴圈,重複檢查Socket的「isConnected」是否傳出false,如果是false就重複執行迴圈,直到是true為止。

4.連線完成務必關閉Stream和Socket。(ServerSocket本身不用關閉,要關閉從ServerSocket執行「accept」後取得的Socket。)

其餘細節跟標準Java Socket和Java Stream的使用沒有差別。

2018年1月15日 星期一

【Andoid Facebook SDK】隱私權測試

Test

本APP絕對不會收集使用者的私人隱私,僅使用Facebook的帳號作為快速登入的信任機制。

2017年12月16日 星期六

【AWS & RDB & EC2】讓已經存在的EC2上的專案存取新建立的RDB instance

這實在是很矛盾的一件事,但RDS DB可以讓外界(非AWS的環境)存取,可是自己內部的EC2要連線卻需要設定層層關卡。

(據說,這是安全需要。其實RDS DB都有對外部連線設下層層保護,並不是單純的只靠帳號密碼和Endpoint而已。反而是內部的雲端伺服器群可能會有安全疑慮,所以反而要層層把關。)



如果沒有特別選擇,其實AWS會自動把使用者建立一組default的VPC。(不知道什麼是VPC的人就比較尷尬,因為我不知道怎麼精準正確的解釋。就先假設它是種為了使用者設計的「沙箱式」的防火牆,在這牆內的任何AWS雲端虛擬主機都可以視為在同一個子網域中。──解釋的很爛。)

官方標準Guide文件「Create VPL Connect RDS DB」並沒有提到這點,就好像它們也不會說「虛擬機建好以後不能修改VPC」。──VPC的內容可以隨意修改,Instance的各項設定也可以隨意修改,唯獨一旦指定VPC,除非Instance整個刪除否則這項設定不會再更動。


所以某些情況下,建立了一組EC2虛擬主機後也在上面安裝了資料庫的人,可能會想要改用AWS RDS DB服務,這時可能就會碰到不知從何下手的困擾。

幸運的是:建立EC2時即使沒有新建VPC,系統也會幫忙設定一組VPC,而且這組VPC內的設定一應俱全。有Subnet、也有SecurityGroup。而且在新建RDS DB Instance時也可以選用這組PVC。

所以如果想要讓EC2可以存取使用RDS DB,只要找到這組VPC即可。

(這組VPC的名稱也可以修改,修改後就可以快速找到相對的Subnet和SecurityGroup,然後進行修改。)

(修改的原則跟新建時的原則並沒有太多不同。所以可以參考剩下的官方文件、將剩餘步驟改成「修改現有EC2 Instance」和「新建RDS DB並使用現有VPC」即可。)

2017年12月11日 星期一

【AWS & RDB】設定中文語系

AWS上有「建立資料庫虛擬機」的服務。(就是一個只有資料庫,除此以外啥都沒有的虛擬機。

(簡單使用過了以後的想法是:)XAMPP這樣的軟體或組站法很興盛,所以大家可能會習慣(以為)把資料庫和服務供應器架在一起是件好事,但其實服務不一定會只有一套,例如「後台」和「前台」可以分開成兩套供應器,而且只要資料庫還活著、服務供應器隨時可以整個打掉重新架構。


比較值得一提的是:AWS上的資料庫預設都不支援中文,因為語系設為latin,所以需要調整。

調整的方式是設定RDB Instance所接受的ParameterGroup。

如果沒有自己進行過「新增ParameterGroup」,那RDB Instance會自動幫使用者設為「預設/default」,這組Group內的參數都不能修改。──所以要自己新增一組後,再到Instance下去修改ParamterGroup的「指向」。


怎麼編寫自己的ParameterGroup?建議參考一下default的內容。到(左邊的面板中)ParamterGroup中把deault group打開,(這個default的名稱並不是絕對的,可能大家的名字都不一樣,所以就一律稱為default就好。)可以看到所有的參數被條列呈現。

用「char」做關鍵字,就可以看到有那些跟語系有關的選項。將參數名稱記錄下來、然後在自己新增的group中用手動輸入的方式輸入,就可以有這個屬性。


但是修改RDB Instance的ParamterGroup設定,需要決定「要即刻生效」還是「等排程」,即使選了「即刻生效」,似乎還是要用「Reboot」以後才會讓設定生效,這是比較奇怪的地方。


=============================

AWS的RDB在使用上跟一般SQL幾乎沒有什麼不同。

要指定連線的帳號密碼、用的Driver也是標準SQL Driver。

只是連線用DB Name會在設定Instance時決定,連線用網址(無IP)會在設定完後的Instance詳細資料內用「Endpoint」的名稱顯示。(挺奇妙的命名邏輯。)