Android JNI_OnLoad()函数
JVM加載完本地庫會調用JNI_OnLoad()函數
當Android的 VM(VirtualMachine)執行到C組件(即*so文件)里的System.loadLibrary()函數時,首先會去執行C組件里的JNI_OnLoad()函數。它的用途有二:
1.?????? 告訴VM此C組件使用那一個JNI版本。如果你的*.so文件沒有提供JNI_OnLoad()函數,VM會默認該*.so檔是使用最老的JNI 1.1版本。由于新版的JNI做了許多擴充,如果需要使用JNI的新版功能,例如JNI 1.4的 java.nio.ByteBuffer, 就必須藉由JNI_OnLoad()函數來告知VM。
2.?????? 由于VM執行到System.loadLibrary()函數時,就會立即先呼叫JNI_OnLoad(),所以C組件的開發者可以藉由JNI_OnLoad()來進行C組件內的初期值之設定(Initialization)。
?
例如,在Android的/system/lib/libmedia_jni.so檔案里,就提供了JNI_OnLoad()函數,其程序代碼片段為:
?
//#define?LOG_NDEBUG?0
#define?LOG_TAG?"MediaPlayer-JNI"
………
jint?JNI_OnLoad(JavaVM*?vm,?void*?reserved)
{
????JNIEnv*?env?=?NULL;
????jint?result?=?-1;
?
????if?(vm->GetEnv((void**)?&env,?JNI_VERSION_1_4)?!=?JNI_OK)?{
????????LOGE("ERROR:?GetEnv?failed\n");
????????goto?bail;
????}
????assert(env?!=?NULL);
????if?(register_android_media_MediaPlayer(env)?<?0)?{
????????LOGE("ERROR:?MediaPlayer?native?registration?failed\n");
????????goto?bail;
????}
????if?(register_android_media_MediaRecorder(env)?<?0)?{
????????LOGE("ERROR:?MediaRecorder?native?registration?failed\n");
????????goto?bail;
????}
????if?(register_android_media_MediaScanner(env)?<?0)?{
????????LOGE("ERROR:?MediaScanner?native?registration?failed\n");
????????goto?bail;
????}
????if?(register_android_media_MediaMetadataRetriever(env)?<?0)?{
????????LOGE("ERROR:?MediaMetadataRetriever?native?registration?failed\n");
????????goto?bail;
????}
????/*?success?--?return?valid?version?number?*/
????result?=?JNI_VERSION_1_4;
bail:
????return?result;
}
//?KTHXBYE
?
??? 此函數回傳JNI_VERSION_1_4值給VM,于是VM知道了其所使用的JNI版本了。此外,它也做了一些初期的動作(可呼叫任何本地函數),例如指令:
?
???if?(register_android_media_MediaPlayer(env)?<?0)?{
????????LOGE("ERROR:?MediaPlayer?native?registration?failed\n");
????????goto?bail;
????}
?
就將此組件提供的各個本地函數(NativeFunction)登記到VM里,以便能加快后續呼叫本地函數之效率。
??? JNI_OnUnload()函數與JNI_OnLoad()相對應的。在加載C組件時會立即呼叫JNI_OnLoad()來進行組件內的初期動作;而當VM釋放該C組件時,則會呼叫JNI_OnUnload()函數來進行善后清除動作。當VM呼叫JNI_OnLoad()或JNI_Unload()函數時,都會將VM的指標(Pointer)傳遞給它們,其參數如下:
?
jint?JNI_OnLoad(JavaVM*?vm,?void*?reserved)
{
????? ………
}
?
jint?JNI_OnUnload(JavaVM*?vm,?void*?reserved)
{
????? ………
}
?
在JNI_OnLoad()函數里,就透過VM之指標而取得JNIEnv之指標值,并存入env指針變量里,如下述指令:
?
jint?JNI_OnLoad(JavaVM*?vm,?void*?reserved)
{
????JNIEnv*?env?=?NULL;
????jint?result?=?-1;
?
????if?(vm->GetEnv((void**)?&env,?JNI_VERSION_1_4)?!=?JNI_OK)?{
????????LOGE("ERROR:?GetEnv?failed\n");
????????goto?bail;
????}
?
???由于VM通常是多執行緒(Multi-threading)的執行環境。每一個執行緒在呼叫JNI_OnLoad()時,所傳遞進來的JNIEnv指標值都是不同的。為了配合這種多執行緒的環境,C組件開發者在撰寫本地函數時,可藉由JNIEnv指標值之不同而避免執行緒的數據沖突問題,才能確保所寫的本地函數能安全地在Android的多執行緒VM 里安全地執行?;谶@個理由,當在呼叫C組件的函數時,都會將JNIEnv指標值傳遞給它,如下:
jint?JNI_OnLoad(JavaVM*?vm,?void*?reserved)
{
JNIEnv*?env?=?NULL;
? ……….
????if?(register_android_media_MediaPlayer(env)?<?0)?{
???????…….
????}
???? }
這JNI_OnLoad()呼叫register_android_media_MediaPlayer(env)函數時,就將env指標值傳遞過去。如此,在register_android_media_MediaPlayer()函數就能藉由該指標值而區別不同的執行緒,以便化解數據沖突的問題。
????? 例如,在register_android_media_MediaPlayer()函數里,可撰寫下述指令:
???????if ((*env)->MonitorEnter(env, obj) != JNI_OK) {
????????????………
?}
?
? ??查看是否已經有其它執行緒進入此對象,如果沒有,此執行緒就進入該對象里執行了。還有,也可撰寫下述指令:
?
????????if ((*env)->MonitorExit(env, obj) != JNI_OK) {
?????????????………
?????????}
?
查看是否此執行緒正在此對象內執行,如果是,此執行緒就會立即離開。
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Android JNI_OnLoad()函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: svn patch
- 下一篇: 一个成功的Git分支模型