Android跨進(jìn)程通信IPC整體內(nèi)容如下
- 1、Android跨進(jìn)程通信IPC之1——Linux基礎(chǔ)
- 2、Android跨進(jìn)程通信IPC之2——Bionic
- 3、Android跨進(jìn)程通信IPC之3——關(guān)于"JNI"的那些事
- 4、Android跨進(jìn)程通信IPC之4——AndroidIPC基礎(chǔ)1
- 4、Android跨進(jìn)程通信IPC之4——AndroidIPC基礎(chǔ)2
- 5、Android跨進(jìn)程通信IPC之5——Binder的三大接口
- 6、Android跨進(jìn)程通信IPC之6——Binder框架
- 7、Android跨進(jìn)程通信IPC之7——Binder相關(guān)結(jié)構(gòu)體簡(jiǎn)介
- 8、Android跨進(jìn)程通信IPC之8——Binder驅(qū)動(dòng)
- 9、Android跨進(jìn)程通信IPC之9——Binder之Framework層C++篇1
- 9、Android跨進(jìn)程通信IPC之9——Binder之Framework層C++篇2
- 10、Android跨進(jìn)程通信IPC之10——Binder之Framework層Java篇
- 11、Android跨進(jìn)程通信IPC之11——AIDL
- 12、Android跨進(jìn)程通信IPC之12——Binder補(bǔ)充
- 13、Android跨進(jìn)程通信IPC之13——Binder總結(jié)
- 14、Android跨進(jìn)程通信IPC之14——其他IPC方式
- 15、Android跨進(jìn)程通信IPC之15——感謝
在分析IPC基于Android 6.0)的過(guò)程中,里面的核心部分是Native的,并且還包含一些linux kernel,而作為Android開發(fā)者看到的代碼大部分都是Java層,所以這就一定會(huì)存在Java與C/C++代碼的來(lái)回跳轉(zhuǎn),那么久很有必要來(lái)先說(shuō)一下JNI,本文主要內(nèi)容如下:
- 1、相關(guān)代碼
- 2、JNI簡(jiǎn)介
- 3、Android中應(yīng)用程序框架
- 4、JNI查找方式
- 5、loadLibrary源碼分析
- 6、JNI資源
- 7、總結(jié)
一、相關(guān)代碼
(一)、代碼位置
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
(二)、代碼鏈接
二、JNI簡(jiǎn)介
(一)、JNI介紹
JNI(Java Native Interface,Java本地接口),用于打通Java層與Native(C/C++)層。這不是Android系統(tǒng)獨(dú)有的,而是Java所有。眾所周知,Java語(yǔ)言是是跨平臺(tái)的語(yǔ)言,而這跨平臺(tái)的背后都是一開Java虛擬機(jī),虛擬機(jī)采用C/C++編寫,適配各個(gè)系統(tǒng),通過(guò)JNI為上層Java提供各種服務(wù),保證跨平臺(tái)性。
其實(shí)不少的Java的程序員,享受著其跨平臺(tái)性,可能全然不知JNI的存在。在Android平臺(tái),讓JNI大放異彩,為更多的程序員所數(shù)值,往往為了提供效率或者其他功能需求,就需要在NDK上開發(fā)。本文的主要目的是介紹android上層中Java與Native的紐帶JNI。

(二)、Java/JNI/C的關(guān)系
1、C與Java的側(cè)重
- C語(yǔ)言:C語(yǔ)言中重要的是函數(shù) fuction
- Java語(yǔ)言:Java中最重要的是JVM,class類,以及class中的方法
2、C與Java的"面向"
- C語(yǔ)言:是面向函數(shù)的語(yǔ)言
- Java語(yǔ)言:是面向?qū)ο蟮恼Z(yǔ)言
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ǔ)言的之間的障礙。
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ǔ)言代碼
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ù)定偏移量中。下圖說(shuō)明了接口指針的組織結(jié)構(gòu)。

JNI接口的組織類似于C++虛擬函數(shù)表或COM接口。使用接口表而不實(shí)用硬性編入的函數(shù)表的好處是使JNI名字空間與平臺(tái)代碼分開。虛擬機(jī)可以很容易地提供了多個(gè)版本的JNI寒暑表。例如,虛擬機(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接口指針。
本地方法將JNI接口指針當(dāng)參數(shù)來(lái)接受。虛擬機(jī)在從相同的Java線程對(duì)本地方法進(jìn)行多次調(diào)用時(shí),保證傳遞給本地方法的接口指針是相同的。但是,一個(gè)本地方法可被不同的Java線程所調(diào)用,因此可以接受不同的JNI接口指針。

6、JavaVM和JNIEnv
####### (1)、JavaVM
代表Java虛擬機(jī)。所有的工作都是從獲取虛擬機(jī)接口開始的。有兩種方式:第一種方式,在加載動(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*。
三、Android中應(yīng)用程序框架
(一)、正常情況下的Android框架
最頂層是** Android應(yīng)用程序代碼 **,上層的 ** 應(yīng)用層 ** 和 ** 應(yīng)用框架層 ** 主要是Java代碼,中間有一層的 ** Framework框架層代碼 ** 是C/C++代碼,通過(guò)Framework進(jìn)行系統(tǒng)調(diào)用,調(diào)用底層的庫(kù)和 Linux內(nèi)核
(二)、使用JNI的Android框架
使用JNI時(shí)的Android框架:繞過(guò)Framework提供的底層代碼,直接調(diào)用自己的寫的C代碼,該代碼最終會(huì)編譯成一個(gè)庫(kù),這個(gè)庫(kù)通過(guò)JNI提供的一個(gè)Stable的ABI 調(diào)用Linux kernel,ABI是二進(jìn)制程序接口 application binary interface

(三)、Android框架中的JNI
1、紐帶
JNI是連接框架層(Framework - C/C++) 和應(yīng)用框架層(Application Framework - Java )的紐帶
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開發(fā),主要使用標(biāo)準(zhǔn)的JNI編程模型
- 應(yīng)用框架層: 使用的是Android中自定義的一套JNI編程模型,該自定義的JNI編程模型彌補(bǔ)了標(biāo)準(zhǔn)的JNI編程模型的不足
3、NDK與JNI區(qū)別:
- NDK:NDK是Google開發(fā)的一套開發(fā)和編譯工具集,主要用于AndroidJNI開發(fā)
- JNI:JNI十套編程接口,用來(lái)實(shí)現(xiàn)Java代碼與本地C/C++代碼進(jìn)行交互
四、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è)。
(一)、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ù)
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)的方法
上面提到了gRegJNI數(shù)組,gRegJNI是什么,我們一起來(lái)看下
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)看下
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)的方法。
(二)、如何查找native方法
當(dāng)大家在看framework層代碼時(shí),經(jīng)常會(huì)看到native方法,這往往需要查看所對(duì)應(yīng)的C++方法在那個(gè)文件,對(duì)應(yīng)那個(gè)方法?下面從一個(gè)實(shí)例出發(fā)帶大家如何查看Java層方法所對(duì)應(yīng)的Native方法位置
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ò)程是開機(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。
打開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ò)程。
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
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)
{
jclass clazz;
clazz = env->FindClass("android/media/MediaPlayer");
if (clazz == NULL) {
return;
}
fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.context == NULL) {
return;
}
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.post_event == NULL) {
return;
}
fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");
if (fields.surface_texture == NULL) {
return;
}
env->DeleteLocalRef(clazz);
clazz = env->FindClass("android/net/ProxyInfo");
if (clazz == NULL) {
return;
}
fields.proxyConfigGetHost =
env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");
fields.proxyConfigGetPort =
env->GetMethodID(clazz, "getPort", "()I");
fields.proxyConfigGetExclusionList =
env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");
env->DeleteLocalRef(clazz);
gPlaybackParamsFields.init(env);
gSyncParamsFields.init(env);
}
所以 MediaPlayer.java中的native_init()方法位于/frameworks/base/media/jni/目錄下的android_media_MediaPlayer.cpp文件中的android_media_MediaPlayer_native_init方法。
(三)、總結(jié)
JNI作為連接Java世界和C/C++世界的橋梁,很有必要掌握。看完本文后,一定要掌握在分析Android源碼過(guò)程匯總?cè)绾尾檎襫ative方法。首先明白native方法名和文件名的命名規(guī)律,其次要懂得該如何去搜索代碼。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è)。
五、loadLibrary源碼分析
再來(lái)進(jìn)一步分析,Java層與native層方法是如何注冊(cè)并映射的,繼續(xù)以MediaPlayer為例,進(jìn)一步分析。
在MediaPlayer.java中調(diào)用System.loadLibrary("media_jni"),把libmedia_jni.so動(dòng)態(tài)庫(kù)加載到內(nèi)存。接下來(lái)以loadLibrary為起點(diǎn)展開JNI注冊(cè)流程的過(guò)程分析。
(一) loadLibrary() 流程
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)
那我們就來(lái)跟蹤一下
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)真正的加載。那我們來(lái)一起看一下
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,再細(xì)講就要深入剖析虛擬機(jī)內(nèi)部,這里就不往下深入了。后續(xù)有時(shí)間單獨(dú)講解下虛擬機(jī)。這里直接說(shuō)結(jié)論
- 調(diào)用dlopen()函數(shù),打開一個(gè)so文件并創(chuàng)建一個(gè)handle;
- 調(diào)用dlsym()函數(shù),查看相應(yīng)so文件的JNIOnLoad()函數(shù)指針,并執(zhí)行相應(yīng)函數(shù)。
4、總結(jié)
所以說(shuō),System.loadLibrary()的作用就是調(diào)用相應(yīng)庫(kù)中的JNI_OnLoad()方法。那我們就來(lái)看下JNI_OnLoad()過(guò)程
(二) JNI_OnLoad流程
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è)的。
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ù)。
在看AndroidRuntime的registerNativeMethods()函數(shù)之前,先說(shuō)下gMethods
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;
下面讓我們看一下AndroidRuntime的registerNativeMethods()函數(shù)
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、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ù),那我們繼續(xù)跟蹤
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)部了,就不在深入了。
(三) 總結(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í)例。
六、JNI資源
JNINativeMethod結(jié)構(gòu)體中有一個(gè)字段為signature(簽名),在介紹signature格式之前需要掌握各種數(shù)據(jù)類型在Java層、Native層以及簽名所采用的簽名格式。
(一)、數(shù)據(jù)類型
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 |
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 |
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 |
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 ; |
(二)、其他注意事項(xiàng)
1、垃圾回收
對(duì)于Java而言,開發(fā)者是無(wú)需關(guān)心垃圾回收的,因?yàn)檫@完全由虛擬機(jī)GC來(lái)負(fù)責(zé)垃圾回收,而對(duì)于JNI開發(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)行。
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)正確處理異常而崩潰。
七、總結(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ù)簽名方式。