JNI
JNI是Java本地介面。它定義了管理代碼的方法並與本地代碼互動,直接的說就是java與C/C++的互動。我們應該認真的瞭解一下j2se 6上的JNI。瞭解它的功能。更詳細的資訊可以訪問Jva官網。
JavaVM 和JNIEnv
JNI定義了兩個關鍵的資料結構,JavaVM和JNIEnv。本質上來說你可以吧它們理解為函數表的指標。JavaVM 提供調用介面來建立和銷毀它自己,從理論上來說每個進程允許有多個JavaVMs ,但在android上只允許一個。JNIEnv提供大部分JNI函數。你的本地函數都接收一個JNIEnv 做為第一個參數。
JNIEnv是用於本地線程儲存的。理由是你不能在兩個線程之間共用一個JNIEnv。如果一段代碼沒有方法獲得它的JNIEnv,你就應該共用JavaVM,並使用GetEnv來發現線程的JNIEnv。
在C上聲明JNIEnv 和JavaVM 與C++上的聲明是不同的。"jni.h"
include檔案提供不同的typedefs ,這就取決於你include的是C還是C++了。出於這個原因,在兩種語言的標頭檔中包含JNIEnv 參數是一個不好的做法。
Threads
所有線程都是Linux線程,並由核心調度。它們通常從Managed 程式碼開始(例如 Thread.start),但它們也能從別處建立然後串連到JavaVM。例如,一個線程使用pthread_create
建立,然後被JNI
的AttachCurrentThread
或AttachCurrentThreadAsDaemon
函數來串連。在一個線程被串連之前,它沒有JNIEnv就不能使用JNI調用 。
Android 不會掛起正在執行本地代碼的線程。如果Gc(記憶體回收)正在處理,或者debugger 發出一個掛起請求,android將會在下一次暫停這個線程並使用一個JNI調用。
另外我們需要通過JNI調用DetachCurrentThread
來連接線程,前提是這些線程在退出之前我們就要執行調用。
jclass, jmethodID, jfieldID
如果你想要從本地代碼訪問一個對象的範圍(或者叫屬性),你應該這麼做:
- 使用FindClass獲得一個類對象的引用
- 使用
GetFieldID
獲得一個域ID
- 篩選你需要的域類型,例如使用
GetIntField
同樣的,如果你想調用一個方法,首先你獲得一個類對象的引用然後獲得一個方法ID。這些ID往往都是內部運行時資料結構中的指標。尋找他們可能需要幾個String的比較,一旦你實際調用過它們,那麼獲得範圍或調用方法是非常快的。
如果效能要求比較高的話,尋找這些值並在你的網本地代碼中緩衝結果是很有用的。因為每一個進程只有一個JavaVM的限制,在一個靜態本地結構儲存這些資料是很合理的。
在類被卸載之前,它的引用,範圍ID和方法ID是保證有效。
當一個類被載入時,如果你想要緩衝ID,並且想要當類被卸載和重載時自動重新緩衝它們,正確的方法就是初始化這些ID並添加下面一段代碼:
private static native void nativeInit();
//靜態塊文法,不熟悉的請先補java
static {
nativeInit();
}
請注意上面代碼只執行一次,就是當類被第一次初始化時,不管你這個類被new多少次nativeInit()只執行一次,除非你把這個類卸載掉,在重新載入它才會被執行。
UTF-8 and UTF-16 Strings
java程式語言使用的是UTF-16。為方便起見,JNI提供方法使它修改後工作在UTF-8上。修改後的編碼對C來說是有用的,因為它用0xc0 0x80編碼\u0000而不是用0x00。
如果可能的話,使用UTF-16會更快。