[卷I]第2章 深刻理解JNI

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或許是進程佔用內存過多的一個緣由。

相關文章
相關標籤/搜索