Android跨進(jìn)程通信IPC之4——關(guān)于"JNI"的那些事

移步系列Android跨進(jìn)程通信IPC系列

1 相關(guān)代碼

1.1 代碼位置

frameworks/base/core/jni/AndroidRuntime.cpp

libcore/luni/src/main/java/java/lang/System.java
libcore/luni/src/main/java/java/lang/Runtime.java
libnativehelper/JNIHelp.cpp
libnativehelper/include/nativehelper/jni.h

frameworks/base/core/java/android/os/MessageQueue.java
frameworks/base/core/jni/android_os_MessageQueue.cpp

frameworks/base/core/java/android/os/Binder.java
frameworks/base/core/jni/android_util_Binder.cpp

frameworks/base/media/java/android/media/MediaPlayer.java
frameworks/base/media/jni/android_media_MediaPlayer.cpp

1.2 代碼鏈接

2 JNI簡(jiǎn)介

2.1 JNI介紹

  • JNI(Java Native Interface,Java本地接口),用于打通Java層與Native(C/C++)層。
  • 這不是Android系統(tǒng)獨(dú)有的磕瓷,而是Java所有走越。
  • Java語(yǔ)言是是跨平臺(tái)的語(yǔ)言丰嘉,而這跨平臺(tái)的背后都是一開(kāi)Java虛擬機(jī),虛擬機(jī)采用C/C++編寫(xiě)饱岸,適配各個(gè)系統(tǒng)还棱,通過(guò)JNI為上層Java提供各種服務(wù)载慈,保證跨平臺(tái)性。
  • 往往為了提供效率或者其他功能需求珍手,就需要在NDK上開(kāi)發(fā)
  • android上層中Java與Native的紐帶JNI

2.2 Java/JNI/C的關(guān)系

2.2.1 C與Java的側(cè)重

  • C語(yǔ)言:C語(yǔ)言中重要的是函數(shù) fuction
  • Java語(yǔ)言:Java中最重要的是JVM娃肿,class類,以及class中的方法

2.2.2 C與Java的"面向"

  • C語(yǔ)言:是面向函數(shù)的語(yǔ)言
  • Java語(yǔ)言:是面向?qū)ο蟮恼Z(yǔ)言

2.2.3 C與Java如何交流

  • JNI規(guī)范:C語(yǔ)言與Java語(yǔ)言交流需要一個(gè)適配器珠十,中間件,即JNI凭豪,JNI提供一共規(guī)范
  • C語(yǔ)言中調(diào)用Java的方法:可以讓我們?cè)贑代碼中找到Java代碼class的方法焙蹭,并且調(diào)用該方法
  • Java語(yǔ)言中調(diào)用C語(yǔ)言方法:同時(shí)也可以在Java代碼中,將一個(gè)C語(yǔ)言的方法映射到Java的某個(gè)方法上
  • JNI的橋梁作用:JNI提供了一個(gè)橋梁嫂伞,打通了C語(yǔ)言和Java語(yǔ)言的之間的障礙孔厉。

2.2.4 JNI中的一些概念

  • natvie:Java語(yǔ)言中修飾本地方法的修飾符(也可以理解為關(guān)鍵字),被該修飾符修飾的方法沒(méi)有方法體
  • Native方法:在Java語(yǔ)言中被native關(guān)鍵字修飾的方法是Native方法
  • JNI層:Java聲明Native方法的部分
  • JNI函數(shù):JNIEnv提供的函數(shù)帖努,這些函數(shù)在jni.h中進(jìn)行定義
  • JNI方法:Native方法對(duì)應(yīng)JNI實(shí)現(xiàn)的C/C++方法撰豺,即在jni目錄中實(shí)現(xiàn)的那些C語(yǔ)言代碼

2.2.5 JNI接口函數(shù)和指針

  • 平臺(tái)相關(guān)代碼是通過(guò)調(diào)用JNI函數(shù)來(lái)訪問(wèn)Java虛擬機(jī)功能的。
  • JNI函數(shù)可以通過(guò)接口指針來(lái)獲得拼余。
  • 接口指針是指針的指針污桦,它指向一個(gè)指針數(shù)組,而指針數(shù)組中的每個(gè)元素又指向一個(gè)接口函數(shù)匙监。
  • 每個(gè)接口函數(shù)都處在數(shù)組的某個(gè)預(yù)定偏移量中凡橱。


    5713484-441a0f5a36037683.png
  • JNI接口的組織類似于C++虛擬函數(shù)表或COM接口。使用接口表而不實(shí)用硬性編入的函數(shù)表的好處是使JNI名字空間與平臺(tái)代碼分開(kāi)亭姥。
  • 虛擬機(jī)可以很容易地提供了多個(gè)版本的JNI函數(shù)表稼钩。
  • 例如,虛擬機(jī) 可以支持以下兩個(gè)JNI函數(shù)表:
  • 一個(gè)表對(duì)非法參數(shù)進(jìn)行全面檢查达罗,適用于調(diào)試程序
  • 另一個(gè)表只進(jìn)行JNI規(guī)范所要求的最小程度的檢查坝撑,因此效率較高。
  • JNI接口指針只在當(dāng)前線程中有效
  • 因此巡李,本地方法不能將接口指針從一個(gè)線程傳遞到另一個(gè)線程中抚笔。
  • 實(shí)現(xiàn)JNI虛擬機(jī)可能將本地線程的數(shù)據(jù)分配和儲(chǔ)存在JNI接口指針?biāo)赶騾^(qū)域中。本地方法將JNI接口指針當(dāng)做參數(shù)來(lái)接受击儡。
  • 虛擬機(jī)在從相同的Java線程中對(duì)本地方法進(jìn)行多次調(diào)用時(shí)塔沃,保證傳遞給本地方法的接口指針是相同的。但是阳谍,一個(gè)本地方方可以被不同的Java線程所調(diào)用蛀柴,因此可以接受不同的JNI接口指針。


    5713484-e584e725ea6a6308.png

2.2.6 JavaVM和JNIEnv

(1)矫夯、JavaVM

  • 代表Java虛擬機(jī)鸽疾。所有的工作都是從獲取虛擬機(jī)接口開(kāi)始的。
  • 有兩種方式:
  • 第一種方式训貌,在加載動(dòng)態(tài)鏈接庫(kù)時(shí)制肮,JVM會(huì)調(diào)用JNI_OnLoad(JavaVM * jvm, void * reserved)(如果定了該函數(shù))。第一個(gè)參數(shù)會(huì)傳入JavaVM指針递沪;
  • 第二種方式豺鼻,在native_code中調(diào)用JNI_CreateJavaVM(&jvm,(void*)&env,&vm_args) 可以得到JavaVM指針。
  • 兩種方式都可以用全局變量款慨,比如JavaVM * g_jvm來(lái)保存獲取到的指針以便在任意上下文中使用儒飒。Android系統(tǒng)是利用第二種方式Invocation interface來(lái)創(chuàng)建JVM

(2)、JNIEnv

  • JNIEnv檩奠,即JNIEnvironment桩了;字面意思就是JNI環(huán)境。
  • 其實(shí)它是一個(gè)與線程相關(guān)的JNI環(huán)境結(jié)構(gòu)體埠戳。
  • 所以JNIEnv類型實(shí)際代表了Java環(huán)境井誉。通過(guò)這個(gè)JNIEnv指針,就可以對(duì)Java端代碼進(jìn)行操作*整胃。
  • 與線程相關(guān)颗圣,不同線程的JNIEnv相互獨(dú)立。JNIEnv只在當(dāng)前線程中有效屁使。
  • Native方法不能將JNIEnv從一個(gè)線程傳遞到另一個(gè)線程中欠啤。
  • 相同的Java線程對(duì)Native方法多次調(diào)用時(shí),傳遞給Native方法的JNIEnv是相同的屋灌。但是洁段,一個(gè)本地方法可能會(huì)被不同的Java線程調(diào)用,因此可以接受不同的JNIEnv共郭。
  • 和JNIEnv相比祠丝,JavaVM可以在進(jìn)程中各個(gè)線程間共享疾呻。理論上一個(gè)進(jìn)程可以有多個(gè)JavaVM,但Android只允許一個(gè)JavaVM写半。
  • 需要強(qiáng)調(diào)在Android SDK中強(qiáng)調(diào)了額 " do not cache JNIEnv * "岸蜗,要用的時(shí)候在不同的線程中通過(guò)JavaVM * jvm的方法來(lái)獲取與當(dāng)前線程相關(guān)的JNIEnv
  • 在Java里叠蝇,每一個(gè)一個(gè)process可以產(chǎn)生多個(gè)JavaVM對(duì)象璃岳,但是在android上,每一個(gè)process只有一個(gè)Dalvik虛擬機(jī)對(duì)象悔捶,也就是在android進(jìn)程中是通過(guò)有且只有一個(gè)虛擬機(jī)對(duì)象來(lái)服務(wù)所有Java和C/C++代碼铃慷。
  • Java的dex字節(jié)碼和C/C++的 xxx.so 同時(shí)運(yùn)行Dalvik虛擬機(jī)之內(nèi),共同使用一個(gè)進(jìn)程空間蜕该。之所以可以相互調(diào)用犁柜,也是因?yàn)橛蠨alvik虛擬機(jī)。
  • 當(dāng)Java代碼需要C/C++代碼時(shí)堂淡,Dalvik虛擬機(jī)加載xxx.so庫(kù)時(shí)馋缅,會(huì)先調(diào)用JNI_Onload(),此時(shí)會(huì)把Java對(duì)象的指針存儲(chǔ)于C層JNI組件的全局環(huán)境中绢淀,在Java層調(diào)用C層的Native函數(shù)時(shí)萤悴,調(diào)用Native函數(shù)線程必然通過(guò)Dalvik虛擬機(jī)來(lái)調(diào)用C層的Native函數(shù)。
  • 此時(shí)皆的,虛擬機(jī)會(huì)為Native的C組件是實(shí)例化一個(gè)JNIEnv指針覆履,該指針指向Dalvik虛擬機(jī)的具體函數(shù)列表,當(dāng)JNI的C組件調(diào)用Java層的方法或?qū)傩詴r(shí)祭务,需要JNIEnv指針來(lái)進(jìn)行調(diào)用。
  • 當(dāng)本地C/C++想獲的當(dāng)前線程所要使用的JNIEnv時(shí)怪嫌,可以使用Dalvik虛擬機(jī)對(duì)象的JavaVM * jvm—>GetEnv()返回當(dāng)前線程所在的JNIEnv*义锥。

3 Android中應(yīng)用程序框架

Android系統(tǒng)架構(gòu)及系統(tǒng)源碼目錄

3.1 正常情況下的Android框架

5713484-dafea18493c8d790.png

3.2 使用JNI的Android框架

  • 使用JNI時(shí)的Android框架:繞過(guò)Framework提供的底層代碼,直接調(diào)用自己的寫(xiě)的C代碼岩灭,該代碼最終會(huì)編譯成一個(gè)庫(kù)拌倍,這個(gè)庫(kù)通過(guò)JNI提供的一個(gè)Stable的ABI 調(diào)用Linux kernel,ABI是二進(jìn)制程序接口 application binary interface
    5713484-7f05249cc1ea3d4e.png

3.3 Android框架中的JNI

3.3.1 紐帶

JNI是連接框架層(Framework - C/C++) 和應(yīng)用框架層(Application Framework - Java )的紐帶

3.3.2 JNI在Android中的作用

JNI可以調(diào)用本地代碼庫(kù)(即C/C++代碼)噪径,并通過(guò)Dalvik虛擬機(jī)與應(yīng)用層和應(yīng)用框架層進(jìn)行交互柱恤,Android 中的JNI主要位于應(yīng)用層和應(yīng)用框架層之間

  • 應(yīng)用層:該層是由JNI開(kāi)發(fā),主要使用標(biāo)準(zhǔn)的JNI編程模型
  • 應(yīng)用框架層: 使用的是Android中自定義的一套JNI編程模型找爱,該自定義的JNI編程模型彌補(bǔ)了標(biāo)準(zhǔn)的JNI編程模型的不足

3.3.3 NDK與JNI區(qū)別:

  • NDK:NDK是Google開(kāi)發(fā)的一套開(kāi)發(fā)和編譯工具集梗顺,主要用于AndroidJNI開(kāi)發(fā)
  • JNI:JNI十套編程接口,用來(lái)實(shí)現(xiàn)Java代碼與本地C/C++代碼進(jìn)行交互

4 JNI查找方式

Android系統(tǒng)在啟動(dòng)的過(guò)程中车摄,先啟動(dòng)Kernel創(chuàng)建init進(jìn)程寺谤,緊接著由init進(jìn)程fork第一個(gè)橫穿Java和C/C++的進(jìn)程仑鸥,即Zygote進(jìn)程。Zygote啟動(dòng)過(guò)程會(huì)在 AndroidRuntime.cpp 中的startVM創(chuàng)建虛擬機(jī)变屁,VM創(chuàng)建完成后眼俊,緊接著調(diào)用 startReg 完成虛擬機(jī)中的JNI方法注冊(cè)

4.1 startReg

// frameworks/base/core/jni/AndroidRuntime.cpp      1440行
/*
 * Register android native functions with the VM.
 * 在虛擬機(jī)上注冊(cè)Android的native方法
 */
 int AndroidRuntime::startReg(JNIEnv*env) {
    /*
     * This hook causes all future threads created in this process to be
     * attached to the JavaVM.  (This needs to go away in favor of JNI
     * Attach calls.)
     * 此鉤子將導(dǎo)致在此過(guò)程中創(chuàng)建的所有未來(lái)線程 附加到JavaVM粟关。 (這需要消除對(duì)
     * JNI的支持附加調(diào)用疮胖。)
     * 說(shuō)白了就是設(shè)置線程的創(chuàng)建方法為 javaCreateThreadEtc
     */
        androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
        ALOGV("--- registering native functions ---\n");
    /*
     * Every "register" function calls one or more things that return
     * a local reference (e.g. FindClass).  Because we haven't really
     * started the VM yet, they're all getting stored in the base frame
     * and never released.  Use Push/Pop to manage the storage.
     * 每個(gè)“注冊(cè)”函數(shù)調(diào)用一個(gè)或多個(gè)返回的東西本地引用(例如FindClass)。
     * 因?yàn)槲覀儧](méi)有真的啟動(dòng)虛擬機(jī)闷板,它們都被存儲(chǔ)在基本框架中并沒(méi)有發(fā)布澎灸。 
     * 使用Push / Pop管理存儲(chǔ)。
     */
        env -> PushLocalFrame(200);
        // 進(jìn)程JNI注冊(cè)函數(shù)
        if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
            env -> PopLocalFrame(NULL);
            return -1;
        }
        env -> PopLocalFrame(NULL);
        //createJavaThread("fubar", quickTest, (void*) "hello");
        return 0;
       }

startReg()函數(shù)里面調(diào)用了register_jni_procs()函數(shù)蛔垢,這個(gè)函數(shù)是真正的注冊(cè)击孩,那我們就來(lái)看下這個(gè)register_jni_procs()函數(shù)

4.1.1 register_jni_procs()

// frameworks/base/core/jni/AndroidRuntime.cpp      1283行
    static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv*env) {
        for (size_t i = 0; i < count; i++) {
            if (array[i].mProc(env) < 0) {
            #ifndef NDEBUG
                ALOGD("----------!!! %s failed to load\n", array[i].mName)
            #endif
                return -1;
            }
        }
        return 0;
    }

發(fā)現(xiàn)上面的代碼很簡(jiǎn)單,register_jni_procs(gRegJNI, NELEM(gRegJNI), env)函數(shù)的的作用就是循環(huán)調(diào)用gRegJNI數(shù)組成員所對(duì)應(yīng)的方法

4.1.2 gRegJNI數(shù)組

// frameworks/base/core/jni/AndroidRuntime.cpp      1296行
static const RegJNIRec gRegJNI[] = {
   REG_JNI(register_com_android_internal_os_RuntimeInit),
    REG_JNI(register_android_os_SystemClock),
    REG_JNI(register_android_util_EventLog),
    REG_JNI(register_android_util_Log),
    REG_JNI(register_android_content_AssetManager),
    REG_JNI(register_android_content_StringBlock),
    REG_JNI(register_android_content_XmlBlock),
    REG_JNI(register_android_emoji_EmojiFactory),
    REG_JNI(register_android_text_AndroidCharacter),
    REG_JNI(register_android_text_StaticLayout),
    REG_JNI(register_android_text_AndroidBidi),
    REG_JNI(register_android_view_InputDevice),
    REG_JNI(register_android_view_KeyCharacterMap),
    REG_JNI(register_android_os_Process),
    REG_JNI(register_android_os_SystemProperties),
    REG_JNI(register_android_os_Binder),
    REG_JNI(register_android_os_Parcel),
    REG_JNI(register_android_nio_utils),
    REG_JNI(register_android_graphics_Graphics),
    REG_JNI(register_android_view_DisplayEventReceiver),
    REG_JNI(register_android_view_RenderNode),
    REG_JNI(register_android_view_RenderNodeAnimator),
    REG_JNI(register_android_view_GraphicBuffer),
    REG_JNI(register_android_view_DisplayListCanvas),
    REG_JNI(register_android_view_HardwareLayer),
    REG_JNI(register_android_view_ThreadedRenderer),
    REG_JNI(register_android_view_Surface),
    REG_JNI(register_android_view_SurfaceControl),
    REG_JNI(register_android_view_SurfaceSession),
    REG_JNI(register_android_view_TextureView),
    REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper),
    REG_JNI(register_com_google_android_gles_jni_EGLImpl),
    REG_JNI(register_com_google_android_gles_jni_GLImpl),
    REG_JNI(register_android_opengl_jni_EGL14),
    REG_JNI(register_android_opengl_jni_EGLExt),
    REG_JNI(register_android_opengl_jni_GLES10),
    REG_JNI(register_android_opengl_jni_GLES10Ext),
    REG_JNI(register_android_opengl_jni_GLES11),
    REG_JNI(register_android_opengl_jni_GLES11Ext),
    REG_JNI(register_android_opengl_jni_GLES20),
    REG_JNI(register_android_opengl_jni_GLES30),
    REG_JNI(register_android_opengl_jni_GLES31),
    REG_JNI(register_android_opengl_jni_GLES31Ext),

    REG_JNI(register_android_graphics_Bitmap),
    REG_JNI(register_android_graphics_BitmapFactory),
    REG_JNI(register_android_graphics_BitmapRegionDecoder),
    REG_JNI(register_android_graphics_Camera),
    REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
    REG_JNI(register_android_graphics_Canvas),
    REG_JNI(register_android_graphics_CanvasProperty),
    REG_JNI(register_android_graphics_ColorFilter),
    REG_JNI(register_android_graphics_DrawFilter),
    REG_JNI(register_android_graphics_FontFamily),
    REG_JNI(register_android_graphics_Interpolator),
    REG_JNI(register_android_graphics_LayerRasterizer),
    REG_JNI(register_android_graphics_MaskFilter),
    REG_JNI(register_android_graphics_Matrix),
    REG_JNI(register_android_graphics_Movie),
    REG_JNI(register_android_graphics_NinePatch),
    REG_JNI(register_android_graphics_Paint),
    REG_JNI(register_android_graphics_Path),
    REG_JNI(register_android_graphics_PathMeasure),
    REG_JNI(register_android_graphics_PathEffect),
    REG_JNI(register_android_graphics_Picture),
    REG_JNI(register_android_graphics_PorterDuff),
    REG_JNI(register_android_graphics_Rasterizer),
    REG_JNI(register_android_graphics_Region),
    REG_JNI(register_android_graphics_Shader),
    REG_JNI(register_android_graphics_SurfaceTexture),
    REG_JNI(register_android_graphics_Typeface),
    REG_JNI(register_android_graphics_Xfermode),
    REG_JNI(register_android_graphics_YuvImage),
    REG_JNI(register_android_graphics_pdf_PdfDocument),
    REG_JNI(register_android_graphics_pdf_PdfEditor),
    REG_JNI(register_android_graphics_pdf_PdfRenderer),

    REG_JNI(register_android_database_CursorWindow),
    REG_JNI(register_android_database_SQLiteConnection),
    REG_JNI(register_android_database_SQLiteGlobal),
    REG_JNI(register_android_database_SQLiteDebug),
    REG_JNI(register_android_os_Debug),
    REG_JNI(register_android_os_FileObserver),
    REG_JNI(register_android_os_MessageQueue),
    REG_JNI(register_android_os_SELinux),
    REG_JNI(register_android_os_Trace),
    REG_JNI(register_android_os_UEventObserver),
    REG_JNI(register_android_net_LocalSocketImpl),
    REG_JNI(register_android_net_NetworkUtils),
    REG_JNI(register_android_net_TrafficStats),
    REG_JNI(register_android_os_MemoryFile),
    REG_JNI(register_com_android_internal_os_Zygote),
    REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
    REG_JNI(register_android_hardware_Camera),
    REG_JNI(register_android_hardware_camera2_CameraMetadata),
    REG_JNI(register_android_hardware_camera2_legacy_LegacyCameraDevice),
    REG_JNI(register_android_hardware_camera2_legacy_PerfMeasurement),
    REG_JNI(register_android_hardware_camera2_DngCreator),
    REG_JNI(register_android_hardware_Radio),
    REG_JNI(register_android_hardware_SensorManager),
    REG_JNI(register_android_hardware_SerialPort),
    REG_JNI(register_android_hardware_SoundTrigger),
    REG_JNI(register_android_hardware_UsbDevice),
    REG_JNI(register_android_hardware_UsbDeviceConnection),
    REG_JNI(register_android_hardware_UsbRequest),
    REG_JNI(register_android_hardware_location_ActivityRecognitionHardware),
    REG_JNI(register_android_media_AudioRecord),
    REG_JNI(register_android_media_AudioSystem),
    REG_JNI(register_android_media_AudioTrack),
    REG_JNI(register_android_media_JetPlayer),
    REG_JNI(register_android_media_RemoteDisplay),
    REG_JNI(register_android_media_ToneGenerator),

    REG_JNI(register_android_opengl_classes),
    REG_JNI(register_android_server_NetworkManagementSocketTagger),
    REG_JNI(register_android_ddm_DdmHandleNativeHeap),
    REG_JNI(register_android_backup_BackupDataInput),
    REG_JNI(register_android_backup_BackupDataOutput),
    REG_JNI(register_android_backup_FileBackupHelperBase),
    REG_JNI(register_android_backup_BackupHelperDispatcher),
    REG_JNI(register_android_app_backup_FullBackup),
    REG_JNI(register_android_app_ActivityThread),
    REG_JNI(register_android_app_NativeActivity),
    REG_JNI(register_android_view_InputChannel),
    REG_JNI(register_android_view_InputEventReceiver),
    REG_JNI(register_android_view_InputEventSender),
    REG_JNI(register_android_view_InputQueue),
    REG_JNI(register_android_view_KeyEvent),
    REG_JNI(register_android_view_MotionEvent),
    REG_JNI(register_android_view_PointerIcon),
    REG_JNI(register_android_view_VelocityTracker),

    REG_JNI(register_android_content_res_ObbScanner),
    REG_JNI(register_android_content_res_Configuration),

    REG_JNI(register_android_animation_PropertyValuesHolder),
    REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
    REG_JNI(register_com_android_internal_net_NetworkStatsFactory),
};

gRegJNI數(shù)組鹏漆,有138個(gè)成員變量巩梢,定義在AndroidRuntime.cpp中,該數(shù)組中每一個(gè)成員都代表一類文件的jni映射艺玲,其中REG_JNI是一個(gè)宏定義括蝠,讓我們來(lái)看下

4.1.3 REG_JNI 宏定義

#ifdef NDEBUG
    #define REG_JNI(name)      { name }
    struct RegJNIRec {
        int (*mProc)(JNIEnv*);
    };
#else
    #define REG_JNI(name)      { name, #name }
    struct RegJNIRec {
        int (*mProc)(JNIEnv*);
        const char* mName;
    };
#endif

其中 mProc,就等價(jià)于調(diào)用其參數(shù)名所指向的函數(shù)饭聚,例如
REG_JNI(register_com_android_internal_os_RuntimeInit).mProc 也就是指進(jìn)入 register_com_android_internal_os_RuntimeInit的方法忌警,以此為例,看下面的代碼

int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{
    return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
        gMethods, NELEM(gMethods));
}

//gMethods:java層方法名與jni層的方法的一一映射關(guān)系
static JNINativeMethod gMethods[] = {
    { "nativeFinishInit", "()V",
        (void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
    { "nativeZygoteInit", "()V",
        (void*) com_android_internal_os_RuntimeInit_nativeZygoteInit },
    { "nativeSetExitWithoutCleanup", "(Z)V",
        (void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup },
};

所以REG_JNI就是一個(gè)宏定義秒梳,該宏的作用就是調(diào)用相應(yīng)的方法法绵。

4.2 如何查找native方法

4.2.1 實(shí)例(一)

分析Android消息機(jī)制源碼,遇到MessageQueue.java中有多個(gè)native方法酪碘,比如:

private native void nativePollOnce(long ptr, int timeoutMillis);

這樣要怎么查找那朋譬?主要分為兩個(gè)步驟

  • 第一步:MessageQueue.java的全限定名為android.os.MessageQueue.java。方法名為android.os.MessageQueue.nativePollOnce()兴垦,而相對(duì)應(yīng)的native層方法名只是將點(diǎn)號(hào)替換為下劃線徙赢,所以可得android_os_MessgaeQueue_nativePollOnce()。所以:nativePollOnce---->android_os_MessageQueue_nativePollOnce()
  • 第二步:有了對(duì)應(yīng)的native方法探越,接下來(lái)就需要這個(gè)native方法在那個(gè)文件中狡赐,上面已經(jīng)說(shuō)了,Android系統(tǒng)啟動(dòng)的時(shí)候已經(jīng)注冊(cè)了大量的JNI方法钦幔。

在AndroidRumtime.cpp的gRegJNI數(shù)組枕屉。這些注冊(cè)方法命名方式如下:

register_[包名]_[類名]

那么MessageQueue.java所定義的JNI注冊(cè)方法名應(yīng)該是
register_android_os_MessageQueue ,的確存在于 gRegJNI 數(shù)組鲤氢,說(shuō)明這次JNI注冊(cè)過(guò)程是開(kāi)機(jī)過(guò)程完成的搀庶。
該方法是在 ** AndroidRuntime.cpp ** 申明為extern方法:

extern int register_android_os_MessageQueue(JNIEnv* env);

這些extern方法絕大多數(shù)位于 ** /framework/base/core/jni ** 目錄拐纱,大多數(shù)情況下 native命名方式為

[包名]_[類名].cpp
[包名]_[類名].h

所以 MessageQueue.java--->android_os_MessageQueue.cpp。
打開(kāi)android_os_MessageQueue.cpp文件哥倔,搜索android_os_MessageQueue_nativePollOnce方法秸架,這便找到了目標(biāo)方法:

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

到這里完成了一次從Java層方法搜索到所對(duì)應(yīng)的C++方法的過(guò)程。

4.2.2 實(shí)例(二)

對(duì)于native文件命名方式咆蒿,有時(shí)并非[包名]_[類名].cpp东抹,比如Binder.java,Binder.java所對(duì)應(yīng)的native文件:android_util_Binder.cpp沃测。
比如:Binder.java的native方法缭黔,代碼如下:

public static final native int getCallingPid();

根據(jù)實(shí)例(一)的方式,找到getCallingPid--->android_os_Binder_getCallingPid()蒂破,并且在AndroidRumtime.cpp中的gRegJNI數(shù)組找到了register_android_os_Binder馏谨。按照實(shí)例(一)方式則native中的文件名為android_os_Binder.cpp,可是在/framwork/base/core/jni/目錄下找不到該文件附迷,這是例外的情況惧互。其實(shí)真正的文件名為android_util_Binder.cpp,這就是例外喇伯,這一點(diǎn)有些費(fèi)勁喊儡,不明白為何google打破之前的規(guī)律。

//frameworks/base/core/jni/android_util_Binder.cpp    761行
static jint android_os_Binder_getCallingPid(JNIEnv* env, jobject clazz)
{
    return IPCThreadState::self()->getCallingPid();
}

有人會(huì)問(wèn)稻据,以后遇到打破常規(guī)的文件命名的文件怎么辦艾猜?其實(shí)很簡(jiǎn)單,首先捻悯,先嘗試在/framework/base/core/jni/中搜索匆赃,對(duì)于Binder.java,可以直接搜索Binder關(guān)鍵字今缚,其他類似算柳。如果這里找不到,可以通過(guò)grep全局搜索android_os_Binder_getCallingPid()這個(gè)方法在那個(gè)文件上荚斯。

jni存在的常見(jiàn)目錄:

  • /framework/base/core/jni
  • /framework/base/services/core/jni
  • /framework/base/media/jni

4.2.3 實(shí)例(三)

前面兩種都是在Android系統(tǒng)啟動(dòng)之初埠居,便已經(jīng)注冊(cè)過(guò)JNI所對(duì)應(yīng)的方法查牌。那么如果程序自己定義的JNI方法事期,該如何查看JNI方法所在的位置?下面以MediaPlayer.java為例纸颜,其包名為android.media:

public class MediaPlayer{
    static {
        System.loadLibrary("media_jni");
        native_init();
    }

    private static native final void native_init();
}

通過(guò)static 靜態(tài)代碼塊中的System.loadLibrary()方法來(lái)加載動(dòng)態(tài)庫(kù)兽泣,庫(kù)名為media_jni,Android平臺(tái)則會(huì)自動(dòng)擴(kuò)展成所對(duì)應(yīng)的libmedia_jni.so庫(kù)胁孙。接著通過(guò)關(guān)鍵字native加載native_init方法之前唠倦,便可以在java層直接使用native層方法称鳞。

接下來(lái)便要查看libmedia_jni.so庫(kù)定義所在文件,一般都是通過(guò)android.mk文件中定義的LOCAL_MODULE:= libmedia_jni稠鼻,可以采用grep或者mgrep來(lái)搜索包含libmedia_jni字段的Android.mk所在路徑冈止。

搜索可知,libmedia_jni.so位于/framework/base/media/jni/Android.mk候齿。用前面的實(shí)例(一)中的知識(shí)來(lái)查看相應(yīng)的文件和方法分別為:

android_media_MediaPlayer.cpp
android_media_MediaPlayer_native_init()

再然后熙暴,你會(huì)發(fā)現(xiàn)果然在該Android.mk所在目錄/frameworks/base/media/jni中找到android_media_MediaPlayer.cpp文件。并在文件中存在相應(yīng)的方法

//frameworks/base/media/jni/android_media_MediaPlayer.cpp     820行
    // This function gets some field IDs, which in turn causes class initialization.
    // It is called from a static block in MediaPlayer, which won't run until the
    // first time an instance of this class is used.
    // 當(dāng)類初始化的時(shí)候此函數(shù)獲取一些字段ID慌盯。因?yàn)樗窃贛ediaPlayer中的靜態(tài)塊中調(diào)用的周霉,所以除非是第一次使用此類的實(shí)例,否則它將不會(huì)運(yùn)行亚皂。
    static void android_media_MediaPlayer_native_init(JNIEnv *env)
    {
      ...
    }

所以 MediaPlayer.java中的native_init()方法位于/frameworks/base/media/jni/目錄下的android_media_MediaPlayer.cpp文件中的android_media_MediaPlayer_native_init方法俱箱。

4.3 總結(jié)

JNI方式注冊(cè)無(wú)非是Android系統(tǒng)啟動(dòng)中Zygote注冊(cè)以及通過(guò)System.loadLibrary方式注冊(cè),對(duì)于系統(tǒng)啟動(dòng)過(guò)程注冊(cè)的灭必,可以通過(guò)查詢AndroidRuntime.cpp中的gRegJNI是否存在對(duì)應(yīng)的register方法狞谱,如果不存在,則大多數(shù)通過(guò)LoadLibrary方式來(lái)注冊(cè)厂财。

5 loadLibrary源碼分析

Java層與native層方法是如何注冊(cè)并映射的芋簿,繼續(xù)以MediaPlayer為例,進(jìn)一步分析璃饱。

5.1 loadLibrary() 流程

5.1.1 loadLibrary()方法

//libcore/luni/src/main/java/java/lang/System.java     1075行
    /**
     * Loads the system library specified by the <code>libname</code>
     * argument. The manner in which a library name is mapped to the
     * actual system library is system dependent.
     *
     * 加載由 libname 參數(shù)指定的系統(tǒng)庫(kù)与斤, library庫(kù)名是通過(guò)系統(tǒng)依賴映射到實(shí)際系統(tǒng)庫(kù)的。
     *
     * <p>
     * The call <code>System.loadLibrary(name)</code> is effectively
     * equivalent to the call
     * <blockquote><pre>
     * Runtime.getRuntime().loadLibrary(name)
     * </pre></blockquote>
     *
     * 調(diào)用 System.loadLibrary(name)實(shí)際上等價(jià)于調(diào)用
     * Runtime.getRuntime().loadLibrary(name)
     *
     * @param      libname   the name of the library.      lib庫(kù)的名字
     * @exception  SecurityException  if a security manager exists and its
     *             <code>checkLink</code> method doesn't allow
     *             loading of the specified dynamic library
     * 如果存在安全管理員荚恶,并且其 checkLink 方法不允許 加載指定的動(dòng)態(tài)庫(kù)撩穿,則會(huì)拋出SecurityException
     * @exception  UnsatisfiedLinkError  if the library does not exist.
     * 如果庫(kù)不存在則拋出UnsatisfiedLinkError
     * @exception  NullPointerException if <code>libname</code> is
     *             <code>null</code>
     * 如果libname是null則拋出NullPointerException
     * @see        java.lang.Runtime#loadLibrary(java.lang.String)
     * @see        java.lang.SecurityManager#checkLink(java.lang.String)
     */
    public static void loadLibrary(String libname) {
         Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
    }

通過(guò)代碼和上面的注釋,我們知道了谒撼,loadLibrary(String)其本質(zhì)是調(diào)用了 Runtime.getRuntime().loadLibrary(String,ClassLoader)

5.1.2 Runtime.getRuntime().loadLibrary(String,ClassLoader)方法

/*
      * Searches for and loads the given shared library using the given ClassLoader.
      * 使用指定的ClassLoader搜索并加載給定的共享庫(kù)
      */
    void loadLibrary(String libraryName, ClassLoader loader) {
         //如果load不為null 則進(jìn)入該分支 
        if (loader != null) {
            //查找?guī)焖诘穆窂?            String filename = loader.findLibrary(libraryName);
            // 如果路徑為null則說(shuō)明找不到庫(kù)
            if (filename == null) {
                // It's not necessarily true that the ClassLoader used
                // System.mapLibraryName, but the default setup does, and it's
                // misleading to say we didn't find "libMyLibrary.so" when we
                // actually searched for "liblibMyLibrary.so.so".
                // 當(dāng)我們搜索liblibMyLibrary.so.so的時(shí)候食寡,可能會(huì)提示我們沒(méi)有找
                // 到"ibMyLibrary.so"。是因?yàn)镃lassClassLoader不一定使用System廓潜,
                 // 但是默認(rèn)設(shè)置又是這樣的抵皱,所以會(huì)有一定的誤導(dǎo)性
                throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
                        System.mapLibraryName(libraryName) + "\"");
            }
            //找到路徑,則加載庫(kù)
            String error = doLoad(filename, loader);
            if (error != null) {
                throw new UnsatisfiedLinkError(error);
            }
            return;
        }
        // 如果loader為null,則執(zhí)行下面的代碼
        // 其中 System.mapLibraryName(String) 是"根據(jù)庫(kù)名返回本地的庫(kù)"
        String filename = System.mapLibraryName(libraryName);
        List<String> candidates = new ArrayList<String>();
        String lastError = null;
        for (String directory : mLibPaths) {
            String candidate = directory + filename;
            candidates.add(candidate);
            if (IoUtils.canOpenReadOnly(candidate)) {
                //加載庫(kù)
                String error = doLoad(candidate, loader);
                if (error == null) {
                    return; // We successfully loaded the library. Job done.
                }
                lastError = error;
            }
        }
        if (lastError != null) {
            throw new UnsatisfiedLinkError(lastError);
        }
        throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
    }

通過(guò)上面的代碼掀潮,我們知道防嗡,無(wú)論loader是否為null,最后都是通過(guò)doLoad(String, ClassLoader)來(lái)真正的加載伤为。

5.1.3 doLoad(String name, ClassLoader loader) 方法

//libcore/luni/src/main/java/java/lang/Runtime.java            401行
    private String doLoad(String name, ClassLoader loader) {
        // Android apps are forked from the zygote, so they can't have a custom LD_LIBRARY_PATH,
        // which means that by default an app's shared library directory isn't on LD_LIBRARY_PATH.
       // Android應(yīng)用程序從zygote分支fork出來(lái)的,所以他們無(wú)法自定義
       // LD_LIBRARY_PATH据途,這意味著默認(rèn)情況下绞愚,應(yīng)用程序的共享庫(kù)目錄不在
       // LD_LIBRARY_PATH上叙甸。

        // The PathClassLoader set up by frameworks/base knows the appropriate path, so we can load
        // libraries with no dependencies just fine, but an app that has multiple libraries that
        // depend on each other needed to load them in most-dependent-first order.

        // We added API to Android's dynamic linker so we can update the library path used for
        // the currently-running process. We pull the desired path out of the ClassLoader here
        // and pass it to nativeLoad so that it can call the private dynamic linker API.

        // We didn't just change frameworks/base to update the LD_LIBRARY_PATH once at the
        // beginning because multiple apks can run in the same process and third party code can
        // use its own BaseDexClassLoader.

        // We didn't just add a dlopen_with_custom_LD_LIBRARY_PATH call because we wanted any
        // dlopen(3) calls made from a .so's JNI_OnLoad to work too.

        // So, find out what the native library search path is for the ClassLoader in question...
        // 由framework / base設(shè)置的PathClassLoader知道適具體的路徑,因此我們可以
        // 加載沒(méi)有依賴關(guān)系的庫(kù)位衩,但是具有依賴于彼此的多個(gè)庫(kù)的應(yīng)用程序需要以大多
        // 數(shù)依賴的順序加載它們裆蒸。 
        // 為了讓我們可以在正在運(yùn)行的進(jìn)程中更新庫(kù)路徑,所以我們向Android的動(dòng)態(tài)鏈
        // 接器添加了API糖驴。
        // 我們將所需的路徑從ClassLoader中拉出光戈,并將其傳遞給nativeLoad,便可以  
        // 調(diào)用私有動(dòng)態(tài)鏈接器API遂赠。
        // 我們不僅僅是更改框架/基礎(chǔ)來(lái)更新LD_LIBRARY_PATH一次久妆,因?yàn)槎鄠€(gè)apk
        // 可以在同一進(jìn)程中運(yùn)行,第三方代碼可以使用自己的BaseDexClassLoader跷睦。
        // 我們沒(méi)有添加dlopen_with_custom_LD_LIBRARY_PATH調(diào)用筷弦,因?yàn)槲覀兿M?        // 使用.so的JNI_OnLoad進(jìn)行的任何dlopen(3)調(diào)用也可以工作。
        //因此抑诸,找出本機(jī)庫(kù)搜索路徑對(duì)于有問(wèn)題的ClassLoader

        String ldLibraryPath = null;
        String dexPath = null;
        if (loader == null) {
            // We use the given library path for the boot class loader. This is the path
            // also used in loadLibraryName if loader is null.
            ldLibraryPath = System.getProperty("java.library.path");
        } else if (loader instanceof BaseDexClassLoader) {
            BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
            ldLibraryPath = dexClassLoader.getLdLibraryPath();
        }
        // nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless
        // of how many ClassLoaders are in the system, but dalvik doesn't support synchronized
        // internal natives.
        // nativeLoad應(yīng)該同步烂琴,所以使用中只有一個(gè)LD_LIBRARY_PATH,無(wú)論系統(tǒng)中有多少個(gè)ClassLoaders蜕乡,dalvik不支持native的同步奸绷。
        synchronized (this) {
            return nativeLoad(name, loader, ldLibraryPath);
        }
    }

nativeLoad()這是一個(gè)native方法,再進(jìn)去ART虛擬機(jī)java_lang_Runtime.cc
直接說(shuō)結(jié)論

  • 調(diào)用dlopen()函數(shù)层玲,打開(kāi)一個(gè)so文件并創(chuàng)建一個(gè)handle;
  • 調(diào)用dlsym()函數(shù)号醉,查看相應(yīng)so文件的JNIOnLoad()函數(shù)指針,并執(zhí)行相應(yīng)函數(shù)辛块。

5.1.4 總結(jié)

System.loadLibrary()的作用就是調(diào)用相應(yīng)庫(kù)中的JNI_OnLoad()方法畔派。

5.2 JNI_OnLoad流程

5.2.1 JNI_OnLoad()

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    //frameworks/base/media/jni/android_media_MediaPlayer.cpp    1132行
    // 注冊(cè)JNI
    if (register_android_media_MediaPlayer(env) < 0) {
        goto bail;
    }
    ...
}

這里面主要通過(guò)調(diào)用register_android_media_MediaPlayer()函數(shù)來(lái)進(jìn)行注冊(cè)的。

5.2.2 register_android_media_MediaPlayer()

//frameworks/base/media/jni/android_media_MediaPlayer.cpp 1086行
// This function only registers the native method
// 這個(gè)函數(shù)僅僅是用來(lái)注冊(cè)native方法的
static int register_android_media_MediaPlayer(JNIEnv *env)
{
    return AndroidRuntime::registerNativeMethods(env,
                "android/media/MediaPlayer", gMethods, NELEM(gMethods));
}

register_android_media_MediaPlayer()函數(shù)里面润绵,實(shí)際上是調(diào)用的AndroidRuntime的registerNativeMethods()函數(shù)线椰。

5.2.3 gMethods

//frameworks/base/media/jni/android_media_MediaPlayer.cpp    1036行
static JNINativeMethod gMethods[] = {
    {
        "nativeSetDataSource",
        "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
        "[Ljava/lang/String;)V",
        (void *)android_media_MediaPlayer_setDataSourceAndHeaders
            
    },
    {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
    {"_setDataSource",      "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
    {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},
    {"_prepare",            "()V",                              (void *)android_media_MediaPlayer_prepare},
    {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},
    {"_start",              "()V",                              (void *)android_media_MediaPlayer_start},
    {"_stop",               "()V",                              (void *)android_media_MediaPlayer_stop},
    {"getVideoWidth",       "()I",                              (void *)android_media_MediaPlayer_getVideoWidth},
    {"getVideoHeight",      "()I",                              (void *)android_media_MediaPlayer_getVideoHeight},
    {"setPlaybackParams", "(Landroid/media/PlaybackParams;)V", (void *)android_media_MediaPlayer_setPlaybackParams},
    {"getPlaybackParams", "()Landroid/media/PlaybackParams;", (void *)android_media_MediaPlayer_getPlaybackParams},
    {"setSyncParams",     "(Landroid/media/SyncParams;)V",  (void *)android_media_MediaPlayer_setSyncParams},
    {"getSyncParams",     "()Landroid/media/SyncParams;",   (void *)android_media_MediaPlayer_getSyncParams},
    {"seekTo",              "(I)V",                             (void *)android_media_MediaPlayer_seekTo},
    {"_pause",              "()V",                              (void *)android_media_MediaPlayer_pause},
    {"isPlaying",           "()Z",                              (void *)android_media_MediaPlayer_isPlaying},
    {"getCurrentPosition",  "()I",                              (void *)android_media_MediaPlayer_getCurrentPosition},
    {"getDuration",         "()I",                              (void *)android_media_MediaPlayer_getDuration},
    {"_release",            "()V",                              (void *)android_media_MediaPlayer_release},
    {"_reset",              "()V",                              (void *)android_media_MediaPlayer_reset},
    {"_setAudioStreamType", "(I)V",                             (void *)android_media_MediaPlayer_setAudioStreamType},
    {"_getAudioStreamType", "()I",                              (void *)android_media_MediaPlayer_getAudioStreamType},
    {"setParameter",        "(ILandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_setParameter},
    {"setLooping",          "(Z)V",                             (void *)android_media_MediaPlayer_setLooping},
    {"isLooping",           "()Z",                              (void *)android_media_MediaPlayer_isLooping},
    {"_setVolume",          "(FF)V",                            (void *)android_media_MediaPlayer_setVolume},
    {"native_invoke",       "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
    {"native_setMetadataFilter", "(Landroid/os/Parcel;)I",      (void *)android_media_MediaPlayer_setMetadataFilter},
    {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_getMetadata},
    {"native_init",         "()V",                              (void *)android_media_MediaPlayer_native_init},
    {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer_native_setup},
    {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer_native_finalize},
    {"getAudioSessionId",   "()I",                              (void *)android_media_MediaPlayer_get_audio_session_id},
    {"setAudioSessionId",   "(I)V",                             (void *)android_media_MediaPlayer_set_audio_session_id},
    {"_setAuxEffectSendLevel", "(F)V",                          (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
    {"attachAuxEffect",     "(I)V",                             (void *)android_media_MediaPlayer_attachAuxEffect},
    {"native_pullBatteryData", "(Landroid/os/Parcel;)I",        (void *)android_media_MediaPlayer_pullBatteryData},
    {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I",  (void *)android_media_MediaPlayer_setRetransmitEndpoint},
    {"setNextMediaPlayer",  "(Landroid/media/MediaPlayer;)V",   (void *)android_media_MediaPlayer_setNextMediaPlayer},
};

gMethods,記錄java層和C/C++層方法的一一映射關(guān)系尘盼。這里涉及到結(jié)構(gòu)體JNINativeMethod憨愉,其定義在jni.h文件:

/、libnativehelper/include/nativehelper/jni.h    129行
typedef struct {
    const char* name;  //Java層native函數(shù)名
    const char* signature;   //Java函數(shù)簽名卿捎,記錄參數(shù)類型和個(gè)數(shù)配紫,以及返回值類型
    void*       fnPtr;  //Native層對(duì)應(yīng)的函數(shù)指針
} JNINativeMethod;

5.2.4 AndroidRuntime的registerNativeMethods()函數(shù)

//frameworks/base/core/jni/AndroidRuntime.cpp  262行
/*
 * Register native methods using JNI.
 * 使用JNI注冊(cè)native方法
 */
int AndroidRuntime::registerNativeMethods(JNIEnv* env,
    const char* className, const JNINativeMethod* gMethods, int numMethods)
{
    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}

jniRegisterNativeMethods該方法是由Android JNI幫助類JNIHelp.cpp來(lái)完成。

5.2.5 jniRegisterNativeMethods()函數(shù)

//libnativehelper/JNIHelp.cpp     73行
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
    const JNINativeMethod* gMethods, int numMethods)
{
    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);

    ALOGV("Registering %s's %d native methods...", className, numMethods);
    scoped_local_ref<jclass> c(env, findClass(env, className));
    //找不到native注冊(cè)方法
    if (c.get() == NULL) {
        char* msg;
        asprintf(&msg, "Native registration unable to find class '%s'; aborting...", className);
        e->FatalError(msg);
    }
     //調(diào)用JNIEnv結(jié)構(gòu)體的成員變量
    if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
        //如果native方法注冊(cè)失敗
        char* msg;
        asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className);
        e->FatalError(msg);
    }

    return 0;
}

通過(guò)上面的代碼我們發(fā)現(xiàn)jniRegisterNativeMethods()內(nèi)部真正的注冊(cè)函數(shù)是RegisterNatives()函數(shù)

5.2.6 RegisterNatives()函數(shù)

// libnativehelper/include/nativehelper/jni.h         976行
    jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
        jint nMethods)
    { return functions->RegisterNatives(this, clazz, methods, nMethods); }
}

其中functions是指向JNINativeInterface結(jié)構(gòu)體指針娇澎,也就是將調(diào)用下面的方法:

struct JNINativeInterface {
    jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,jint);
}

再往下深入就到了虛擬機(jī)內(nèi)部了笨蚁,就不在深入了睹晒。

5.3 總結(jié)

總之趟庄,這個(gè)過(guò)程完成了gMethods數(shù)組中的方法的映射關(guān)系括细,比如java層的native_init()方法,映射到native層的android_media_MediaPlayer_native_init()方法戚啥。

虛擬機(jī)相關(guān)的變量中有兩個(gè)非常重要的變量JavaVM和JNIEnv:

  • JavaVM:是指進(jìn)程虛擬機(jī)環(huán)境奋单,每個(gè)進(jìn)程有且只有一個(gè)JavaVM實(shí)例。
  • JNIEnv:是指線程上下文環(huán)境猫十,每個(gè)線程有且只有一個(gè)JNIEnv實(shí)例览濒。

6 JNI資源

JNINativeMethod結(jié)構(gòu)體中有一個(gè)字段為signature(簽名),在介紹signature格式之前需要掌握各種數(shù)據(jù)類型在Java層拖云、Native層以及簽名所采用的簽名格式贷笛。

6.1 數(shù)據(jù)類型

6.1.1 基本數(shù)據(jù)類型

Signature Java Native
B byte jbyte
C char jchar
D double jdouble
F float jfloat
I int jint
S short jshort
J long jlong
Z boolean jboolean
V void void

6.1.2數(shù)組數(shù)據(jù)類型

數(shù)組簡(jiǎn)稱則是在前面添加" **[ ** " :

Signature格式 Java Native
[B byte[] jbyteArray
[C char[] jcharArray
[D double[] jdoubleArray
[F float[] jfloatArray
[I int[] jintArray
[S shor[] jshortArray
[ J long[] jlongArray
[ Z boolean[] jbooleanArray

6.1.3復(fù)雜數(shù)據(jù)類型

對(duì)象類型簡(jiǎn)稱:L+ ** classname **+;

Signature格式 Java Native
Ljava/lang/String String jstring
L+class+ ; 所有對(duì)象 jobject
[ L + classname + ; Object[] jobjectArray
Ljava.lang.Class Class jclass
Ljava.lang.Throwable Throwable jthrowable

6.1.4 Signature

我們通過(guò)案例來(lái)說(shuō)說(shuō)函數(shù)簽名:** (入?yún)? 返回值參數(shù) **,這里用到的便是前面介紹的Signature格式

Java格式 對(duì)應(yīng)的簽名
void foo() ()V
float foo(int i) (I) F
long foo(int[] i) ([ i) J
double foo (Class c) (Ljava/lang/Class;) D
boolean foo (int [] i,String s) ([ILjava/lang/String ;) Z
String foo(int i) (I) Ljava/lang/String ;

6.2 其他注意事項(xiàng)

6.2.1 垃圾回收

對(duì)于Java而言宙项,開(kāi)發(fā)者是無(wú)需關(guān)心垃圾回收的乏苦,因?yàn)檫@完全由虛擬機(jī)GC來(lái)負(fù)責(zé)垃圾回收,而對(duì)于JNI開(kāi)發(fā)人員尤筐,由于內(nèi)存釋放需要謹(jǐn)慎處理汇荐,需要的時(shí)候申請(qǐng),使用完后記得釋放內(nèi)存盆繁,以免發(fā)生內(nèi)存泄露掀淘。

所以JNI提供了了三種Reference類型:

  • Local Reference(本地引用)
  • Global Reference(全局引用)
  • Weak Global Reference (全局弱引用)
  • 其中Global Reference 如果不主動(dòng)釋放,則一直不會(huì)釋放油昂;
  • 對(duì)于其他兩個(gè)類型的引用都是釋放的可能性革娄,那是不是意味著不需要手動(dòng)釋放? 答案是否定的,不管這三種類型的那種引用冕碟,都盡可能在某個(gè)內(nèi)存不需要時(shí)稠腊,立即釋放,這對(duì)系統(tǒng)更為安全可靠鸣哀,以減少不可預(yù)知的性能與穩(wěn)定性問(wèn)題架忌。

另外,ART虛擬機(jī)在GC算法有所優(yōu)化我衬,為了減少內(nèi)存碎片化問(wèn)題叹放,在GC之后有可能會(huì)移動(dòng)對(duì)象內(nèi)存的位置,對(duì)于Java層程序并沒(méi)有影響挠羔,但是對(duì)于JNI程序要注意了井仰,對(duì)于通過(guò)指針來(lái)直接訪問(wèn)內(nèi)存對(duì)象時(shí),Dalvik能正確運(yùn)行的程序破加,ART下未必能正常運(yùn)行俱恶。

6.2.2 異常處理

  • Java層出現(xiàn)異常,虛擬機(jī)會(huì)直接拋出異常,這是需要try... catch 或者繼續(xù)向外throw合是。
  • 對(duì)于JNI出現(xiàn)異常時(shí)了罪,即執(zhí)行到JNIEnv 中某個(gè)函數(shù)異常時(shí),并不會(huì)立即拋出異常來(lái)中斷程序的執(zhí)行聪全,還可以繼續(xù)執(zhí)行內(nèi)存之類的清理工作泊藕,直到返回Java層才會(huì)拋出相應(yīng)的異常。

另外难礼,Dalvik虛擬機(jī)有些情況下JNI函數(shù)出錯(cuò)可能會(huì)返回NULL娃圆,但ATR虛擬機(jī)在出錯(cuò)時(shí)更多是拋出異常。這樣導(dǎo)致的問(wèn)題就可能是在Dalvik版本能正常運(yùn)行的成員蛾茉,在ART虛擬機(jī)上并沒(méi)正確處理異常而崩潰讼呢。

7 總結(jié)

本文主要是通過(guò)實(shí)例,基于Android 6.0源碼分析 JNI原理谦炬,講述JNI核心功能:

  • 介紹了JNI的概念及如何查找JNI方法吝岭,讓大家明白如何從Java層跳轉(zhuǎn)到Native層
  • 分了JNI函數(shù)注冊(cè)流程,進(jìn)異步加深對(duì)JNI的理解
  • 列舉Java與Native一級(jí)函數(shù)簽名方式吧寺。

參考

Android跨進(jìn)程通信IPC之3——關(guān)于"JNI"的那些事

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末窜管,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子稚机,更是在濱河造成了極大的恐慌幕帆,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赖条,死亡現(xiàn)場(chǎng)離奇詭異失乾,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)纬乍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)碱茁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人仿贬,你說(shuō)我怎么就攤上這事纽竣。” “怎么了茧泪?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵蜓氨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我队伟,道長(zhǎng)穴吹,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任嗜侮,我火速辦了婚禮港令,結(jié)果婚禮上啥容,老公的妹妹穿的比我還像新娘。我一直安慰自己顷霹,他們只是感情好咪惠,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著泼返,像睡著了一般。 火紅的嫁衣襯著肌膚如雪姨拥。 梳的紋絲不亂的頭發(fā)上绅喉,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音叫乌,去河邊找鬼柴罐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛憨奸,可吹牛的內(nèi)容都是我干的革屠。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼排宰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼似芝!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起板甘,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤党瓮,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后盐类,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體寞奸,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年在跳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了枪萄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猫妙,死狀恐怖瓷翻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情割坠,我是刑警寧澤逻悠,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站韭脊,受9級(jí)特大地震影響童谒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜沪羔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一饥伊、第九天 我趴在偏房一處隱蔽的房頂上張望象浑。 院中可真熱鬧,春花似錦琅豆、人聲如沸愉豺。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蚪拦。三九已至,卻和暖如春冻押,著一層夾襖步出監(jiān)牢的瞬間驰贷,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工洛巢, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留括袒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓稿茉,卻偏偏與公主長(zhǎng)得像锹锰,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子漓库,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容