Android native調用分析(JNI)

1. 爲什麼需要native?

2. java層(或者說dalvik)如何識別native函數?

3. java層到native的調用是如何實現的?

4. java和native工作在同一進程嗎?

5. native 受dalvik管理嗎?


帶着這些問題,接下來逐步去分析總結,相信是會有收穫的。


一、爲什麼需要native。

       1.  不可反編譯。native 編譯出來的是目標代碼,不可反編譯。java編譯出來的中間代碼可反編譯,可反編譯可能會導致自己設計思想或者設計算法泄露,很多商業公司是極力避免的。

       2. 執行速度快。native 代碼一般用c/c++ 實現,執行速度上要比java快些。

       3. 方便移植。有些應用在沒有android之前,都是用c/c++ 開發的,那麼現在要移植到android上,直接移植到native層就不需要另外編寫java代碼,省時省力。


二、java(或者說dalvik)如何識別native函數

       native函數聲明會帶有native字符,如下例子:

             private static native long nativeInit(InputManagerService service,
            Context context, MessageQueue messageQueue);

       這樣編譯的時候就把這個native標誌位加入到java 函數符號表裏,這樣就可以和普通的函數區分開了,運行過程虛擬機執行到native函數時就會切換到native執行環境。執行環境是如何切換的呢?還沒研究過。

            

三、java到native的調用是如何實現的

     1. 每個虛擬機進程都對應一個JNI環境(JNIEnv)

     2. 每個native方法都需要向這個JNI環境測試註冊

     3. 這些native方法及實現會被編譯成一個動態庫

     4.java層在執行到native代碼前需要把這個動態庫load進來。之後整個虛擬機進程的JNI環境就包含了native方法入口。

     5. java層在執行到native代碼時就能通過JNI環境知道native層對應實現。


     接下以事件輸入系統爲例做下分析:

    1. native 方法定義數組,格式如下,第一個字段對應的是在java層函數名,第二個是函數形式參數縮寫,第三個是native層對應的函數名。

     

        java 層函數形參和native層是有區別的,需要做轉換。下面是基本類型轉換

縮寫 native 層 java 層
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
[Z jbooleanArray Boolean[]
對象類型轉換:

如下 (JLandroid/view/InputEvent 表示轉下來的是一個java 對象類型
{ "nativeInjectInputEvent", "(JLandroid/view/InputEvent;IIIIII)I",
(void*) nativeInjectInputEvent },

如下Ljava/lang/String 是java字符串類型
"processDirectory",
"(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
(void *)android_media_MediaScanner_processDirectory
在native中需要做如下轉換 const char *pathStr = env->GetStringUTFChars(path, NULL);

2. 註冊方法
第一行把gInputManagerMethods 方法註冊到JNI環境裏,jniRegisterNativeMethods 函數第二個參數意思是這些方法和java層com/android/server/input/InputManagerService 類對應,也就是說將在java層的InputManagerService 類調用這些native方法。
第四行開始做java層到native層的映射,因爲消息不僅僅從java層傳遞到native層,還有可能從native層傳到java層。
FIND_CLASS用來找到java層對應的類;
GET_METHOD_ID 用來找到java層這個類裏對應的方法,並保存起來,如gServiceClassInfo.notifyConfigurationChanged。之後native層就可以直接通過gServiceClassInfo.notifyConfigurationChanged 給java層專遞消息了,其實就是調用java層代碼。

3. 編譯成動態庫
InputManager native層最終會被編譯到libandroid_servers裏面,並放在system/lib下。
4. 加載動態庫。
libandroid_servers 在systemServer裏面加載。這裏只需要提供模塊名就可以了,系統會自動搜索應用lib 目錄下和 vendor/lib system/lib 下是否有對應的庫。
四、 java和native工作在同一進程,一個應用對應一個進程,native是由應用進程load的進來並啓動的,所以肯定都是在同一個進程運行,在同一個進程空間。
五、 native 受dalvik管理嗎
我理解native和dalvik是同一等級的東西,不受dalvik管理。dalvik是用來對java代碼進行解析執行,同時負責堆棧管理,線程管理,對象生命週期管理,內存分配垃圾回收等。dalvik 虛擬機本身也是基於c/c++ 編寫的,是可執行的目標文件,native 層同樣也是可執行的目標文件,所以它們應該是平級的,由linux系統管理運行。因此native層的內存管理,線程管理也是由linux系統管理的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章