JAVA世界 | MediaScanner | MediaScanner.java | native_init();java processFile();android |
JNI層 | libmedia_jni.so | android_media_mediaScanner.cpp | android_media_MediaScanner_native_init |
Natvie世界 | libmedia.so |
關注:native_init 與 android_media_MediaScanner_native_init是如何對應的?註冊。網絡
註冊有靜態註冊和動態註冊兩種:函數
一、靜態註冊,編寫MediaScanner.java java -o javah -o 生成android_media_MediaScanner.hspa
JNIEXPORT void JNICALL Java_android_media_MediaScanner_native_linit線程
JNIEXPORT void JNICALL Java_android_media_MediaScanner_processFile指針
二、動態註冊code
由結構體JNINativeMethod保存對應關係對象
由register_android_media_MediaScanner(JNIEnv *env) 調用註冊函數進程
而register_android_media_MediaScanner由JNI_OnLoad調用(JNI_OnLoad須要本身實現)
由於:當JAVA層經過System.loadLibrary加載完JNI動態庫後,緊接着就會查找庫中的JNI_OnLoad函數。
2.4.3 JNIEnv介紹
JNIEnv 與線程相關的表明JNI環境的結構體。
JNIEnv通常都是native函數轉換成JNI層函數後由虛擬機傳進來的,能夠直接使用。
但若是是後臺線程收到一個網絡消息,而又須要由native層函數主動回調Java層函數時,JNIEnv從何而來?
注意:
jint JNI_OnLoad(JavaVM* vm, void* reserved)
一個進程對應一個JavaVM,即JavaVM是與進程相關的。
調用JavaVM的AttachCurrentThread函數,就可獲得這個線程的JNIEnv結構體。另外,在後臺線程退出前,須要調用JavaVM的DetachCurrentThread函數來釋放對應的資源。
2.4.4 經過JNIEnv操做jobject
先寫一下體會:
在JNI層,先經過GetFieldID,取得在Java層定義的成員變量,如mNativeContext(在MediaScanner.java中private int mNativeContext;)
給jfieldID:fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
且fields是文件中的全局變量。
在Java層會經過native函數調用,到JNI層調用設置來設置Java層的mNativeContext。(呵呵,這是用下層的方法設置本身層的變量啊)。
MediaScanner.java native_setup();
android_media_MediaScanner.cpp android_media_MediaScanner_native_setup env->SetIntField(thiz, fields.context, (int)mp);
固然,設置好後,JNI層能夠經過(MediaScanner *)env->GetIntField(thiz, fields.context);直接使用。
在MediaScanner.java中,代碼邏輯很是明確:
加載完jni庫後調用native_init,會讓jni層取得mNativeContext
建立MediaScanner對象時,會調用native_setup,將StagefrightMediaScanner對象指針給mNativeContext;
對象銷燬時,調用native_finalize,將mNativeContext設爲0。
------------------------------------------------------------------------------------------------------------
JNI層會用jobject來表示對象的數據類型。
在JNI規則中,用jfiledID和jmethodID來表示Java類的成員變量和成員函數,可經過JNIEnv的下面兩個函數獲得:
jfieldID GetField(jclass clazz, const char * name, const char *sig);
jmethodID GetMethodID(jclass clazz, const char * name, const char *sig);
jclass表明java類,name表示成員函數或成員變量的名字,sig爲這個函數和變量的簽名信息。
調用 GetMethodID 保存爲成員變量,爲了程序運行效率。
mScanFileMethodID = env->GetMethodID(
mediaScannerClientInterface,
"scanFile",
"(Ljava/lang/String;JJZZ)V");
取得Java對象的方法scanFile。
mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
fileSize, isDirectory, noMedia);
CallVoidMethod是調用Java對象的函數scanFile。
2.4.5 jstring介紹
調用JNIEnv的NewString(JNIEnv *env, const jchar* unicodeChars, jsize len),能夠從Native的字符串獲得一個jstring對象。
調用JNIEnv的NewStringUTF將根據Native的一個UTF-8字符串獲得一個jstring對象。
GetStringChars GetStringUTFChars 能夠將Java String對象轉換成本地字符串。
須要調用ReleaseStringChars ReleaseStringUTFChars釋放資源, 不然會致使JVM內存泄露。
2.4.7 垃圾回收
JNI技術一共提供三種類型的引用:
Local Reference 本地引用,一旦JNI層函數返回,就可能被垃圾回收。
Global Reference 全局引用,不主動釋放,永遠不會被回收。
Weak Global Reference 弱全局引用,一種特殊的Global Reference,在運行中可能被回收,因此在使用以前須要調用JNIEnv的IsSameObject判斷。
根據Local Reference的說明,函數返回後,對象就會被回收,看起來調用DeleteLocalRef是多餘的,其實,調用DeleteLocalRef是當即回收。
因此,沒有及時回收Local Reference或許是進程佔用內存過多的一個緣由。