|
認(rèn)識(shí)*.so里的JNI_OnLoad()函數(shù) 當(dāng)Android的 VM(Virtual
Machine)執(zhí)行到C組件(即*so文件)里的System.loadLibrary()函數(shù)時(shí),首先會(huì)去執(zhí)行C組件里的JNI_OnLoad()函數(shù)。它的用途有二: 1. 告訴VM此C組件使用那一個(gè)JNI版本。如果你的*.so文件沒(méi)有提供JNI_OnLoad()函數(shù),VM會(huì)默認(rèn)該*.so檔是使用最老的JNI 1.1版本。由于新版的JNI做了許多擴(kuò)充,如果需要使用JNI的新版功能,例如JNI 1.4的 java.nio.ByteBuffer,
就必須藉由JNI_OnLoad()函數(shù)來(lái)告知VM。 2.
由于VM執(zhí)行到System.loadLibrary()函數(shù)時(shí),就會(huì)立即先呼叫JNI_OnLoad(),所以C組件的開發(fā)者可以藉由JNI_OnLoad()來(lái)進(jìn)行C組件內(nèi)的初期值之設(shè)定(Initialization)。
例如,在Android的/system/lib/libmedia_jni.so檔案里,就提供了JNI_OnLoad()函數(shù),其程序代碼片段為:
//#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
此函數(shù)回傳JNI_VERSION_1_4值給VM,于是VM知道了其所使用的JNI版本了。此外,它也做了一些初期的動(dòng)作(可呼叫任何本地函數(shù)),例如指令:
if (register_android_media_MediaPlayer(env) < 0) { LOGE("ERROR: MediaPlayer native registration failed\n"); goto bail; }
就將此組件提供的各個(gè)本地函數(shù)(Native Function)登記到VM里,以便能加快后續(xù)呼叫本地函數(shù)之效率。 JNI_OnUnload()函數(shù)與JNI_OnLoad()相對(duì)應(yīng)的。在加載C組件時(shí)會(huì)立即呼叫JNI_OnLoad()來(lái)進(jìn)行組件內(nèi)的初期動(dòng)作;而當(dāng)VM釋放該C組件時(shí),則會(huì)呼叫JNI_OnUnload()函數(shù)來(lái)進(jìn)行善后清除動(dòng)作。當(dāng)VM呼叫JNI_OnLoad()或JNI_Unload()函數(shù)時(shí),都會(huì)將VM的指標(biāo)(Pointer)傳遞給它們,其參數(shù)如下:
jint JNI_OnLoad(JavaVM* vm, void* reserved) { ……… }
jint JNI_OnUnload(JavaVM* vm, void* reserved) { ……… }
在JNI_OnLoad()函數(shù)里,就透過(guò)VM之指標(biāo)而取得JNIEnv之指標(biāo)值,并存入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通常是多執(zhí)行緒(Multi-threading)的執(zhí)行環(huán)境。每一個(gè)執(zhí)行緒在呼叫JNI_OnLoad()時(shí),所傳遞進(jìn)來(lái)的JNIEnv指標(biāo)值都是不同的。為了配合這種多執(zhí)行緒的環(huán)境,C組件開發(fā)者在撰寫本地函數(shù)時(shí),可藉由JNIEnv指標(biāo)值之不同而避免執(zhí)行緒的數(shù)據(jù)沖突問(wèn)題,才能確保所寫的本地函數(shù)能安全地在Android的多執(zhí)行緒VM 里安全地執(zhí)行?;谶@個(gè)理由,當(dāng)在呼叫C組件的函數(shù)時(shí),都會(huì)將JNIEnv指標(biāo)值傳遞給它,如下: jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; ………. if (register_android_media_MediaPlayer(env) < 0) { ……. } } 這JNI_OnLoad()呼叫register_android_media_MediaPlayer(env)函數(shù)時(shí),就將env指標(biāo)值傳遞過(guò)去。如此,在register_android_media_MediaPlayer()函數(shù)就能藉由該指標(biāo)值而區(qū)別不同的執(zhí)行緒,以便化解數(shù)據(jù)沖突的問(wèn)題。
例如,在register_android_media_MediaPlayer()函數(shù)里,可撰寫下述指令: if
((*env)->MonitorEnter(env, obj) != JNI_OK) { ……… }
查看是否已經(jīng)有其它執(zhí)行緒進(jìn)入此對(duì)象,如果沒(méi)有,此執(zhí)行緒就進(jìn)入該對(duì)象里執(zhí)行了。還有,也可撰寫下述指令:
if
((*env)->MonitorExit(env, obj) != JNI_OK) { ……… }
查看是否此執(zhí)行緒正在此對(duì)象內(nèi)執(zhí)行,如果是,此執(zhí)行緒就會(huì)立即離開。
|
|
|
來(lái)自: lifei_szdz > 《JNI》