JNI是Java本地接口。它定義了管理代碼的方法並與本地代碼互動,直接的說就是java與C/C++的互動。咱們應該認真的瞭解一下j2se 6上的JNI。瞭解它的功能。更詳細的信息能夠訪問Jva官網。 java
JNI定義了兩個關鍵的數據結構,JavaVM和JNIEnv。本質上來講你能夠吧它們理解爲函數表的指針。JavaVM 提供調用接口來建立和銷燬它本身,從理論上來講每一個進程容許有多個JavaVMs ,但在android上只容許一個。JNIEnv提供大部分JNI函數。你的本地函數都接收一個JNIEnv 作爲第一個參數。 android
JNIEnv是用於本地線程存儲的。理由是你不能在兩個線程之間共享一個JNIEnv。若是一段代碼沒有方法得到它的JNIEnv,你就應該共享JavaVM,並使用GetEnv來發現線程的JNIEnv。 緩存
在C上聲明JNIEnv 和JavaVM 與C++上的聲明是不一樣的。"jni.h" include文件提供不一樣的typedefs ,這就取決於你include的是C仍是C++了。出於這個緣由,在兩種語言的頭文件中包含JNIEnv 參數是一個很差的作法。 數據結構
全部線程都是Linux線程,並由內核調度。它們一般從託管代碼開始(例如 Thread.start),但它們也能從別處建立而後鏈接到JavaVM。例如,一個線程使用pthread_create建立,而後被JNI 的AttachCurrentThread 或AttachCurrentThreadAsDaemon 函數來鏈接。在一個線程被鏈接以前,它沒有JNIEnv就不能使用JNI調用 。 多線程
Android 不會掛起正在執行本地代碼的線程。若是Gc(垃圾回收)正在處理,或者debugger 發出一個掛起請求,android將會在下一次暫停這個線程並使用一個JNI調用。 異步
另外咱們須要經過JNI調用DetachCurrentThread來鏈接線程,前提是這些線程在退出以前咱們就要執行調用。 jvm
若是你想要從本地代碼訪問一個對象的值域(或者叫屬性),你應該這麼作: 函數
一樣的,若是你想調用一個方法,首先你得到一個類對象的引用而後得到一個方法ID。這些ID每每都是內部運行時數據結構中的指針。尋找他們可能須要幾個String的比較,一旦你實際調用過它們,那麼得到值域或調用方法是很是快的。 性能
若是性能要求比較高的話,尋找這些值並在你的網本地代碼中緩存結果是頗有用的。由於每個進程只有一個JavaVM的限制,在一個靜態本地結構存儲這些數據是很合理的。 編碼
在類被卸載以前,它的引用,值域ID和方法ID是保證有效的。
當一個類被載入時,若是你想要緩存ID,而且想要當類被卸載和重載時自動從新緩存它們,正確的方法就是初始化這些ID並添加下面一段代碼:
private static native void nativeInit(); //靜態塊語法,不熟悉的請先補java static { nativeInit(); }
請注意上面代碼只執行一次,就是當類被第一次初始化時,無論你這個類被new多少次nativeInit()只執行一次,除非你把這個類卸載掉,在從新載入它纔會被執行。
java程序語言使用的是UTF-16。爲方便起見,JNI提供方法使它修改後工做在UTF-8上。修改後的編碼對C來講是有用的,由於它用0xc0 0x80編碼\u0000而不是用0x00。
若是可能的話,使用UTF-16會更快。
一、JNI異步條件下(多線程/回調函數),如何取得JNIEnv
使用AttachCurrentThread()函數。
示例代碼:
#ifdef JNI_VERSION_1_4 jint res = cached_jvm->AttachCurrentThread((void**)&env, NULL); #else jint res = cached_jvm->AttachCurrentThread(&env, NULL); #endif; if (env == NULL) return; jclass clsSctp = env->FindClass("com/sunrising/nettest/netpackage/implement/SCTPTransImplement"); if (clsSctp == NULL) { ThrowNullPointerException(env, "SCTP class is null"); cached_jvm->DetachCurrentThread(); return; } jmethodID mid = env->GetStaticMethodID(clsSctp, "communicationErrorNotify", "(JJ)V"); if (mid == 0) { ThrowNullPointerException(env, "Method communicationErrorNotify() is null"); cached_jvm->DetachCurrentThread(); return; } env->CallStaticVoidMethod(clsSctp, mid, jlAssociationID, jlErrorType); cached_jvm->DetachCurrentThread();
二、使用GetStringUTFChars()以後要注意釋放內存
使用上述函數以後,要使用ReleaseStringUTFChars()函數釋放字符串內存。
示例代碼:
pBuffer = env->GetStringUTFChars(jsAmrFile, &isCopy); strncpy_s(szAmrFile, MAX_STRING, pBuffer, MAX_STRING - 1); if (isCopy & 0xFF) { env->ReleaseStringUTFChars(jsAmrFile, pBuffer); }