淺析Java中JNI靜態(tài)注冊和動態(tài)注冊
靜態(tài)注冊是指 Java native 方法和對應(yīng)的本地函數(shù)按照預(yù)先定義好的命名規(guī)則來建立關(guān)聯(lián),實現(xiàn)了 Java native 方法與本地函數(shù)實現(xiàn)的綁定,這些函數(shù)會在 Java 類加載時被靜態(tài)注冊到 JVM 中,以確保 JVM 能夠正確地找到對應(yīng)的函數(shù)
動態(tài)注冊是通過提供一個 Java native 方法和本地函數(shù)一一對應(yīng)的映射表,然后在 JNI_OnLoad 回調(diào)里將映射表注冊給 JVM,這樣一來 JVM 就可以用這個映射表來調(diào)用相應(yīng)的函數(shù)了,而不必像靜態(tài)注冊那樣通過函數(shù)名去查找需要調(diào)用的函數(shù),所以本地函數(shù)名不需要遵循特定命名規(guī)則
PS:在 JNI 中靜態(tài)注冊和靜態(tài)庫之間沒有直接的關(guān)系,無論是靜態(tài)注冊還是動態(tài)注冊都可以生成動態(tài)庫(.so文件)或者靜態(tài)庫(.a 文件)
public class MainActivity extends AppCompatActivity { // Used to load the 'myjni' library on application startup. static { System.loadLibrary("myjni"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // String str = stringFromJNI(); Log.e("TAG", "onCreate: str="+str); } /** * A native method that is implemented by the 'myjni' native library, * which is packaged with this application. */ public native String stringFromJNI(); //java native 方法 }
靜態(tài)注冊
本地函數(shù)的函數(shù)名需要遵循 Java_類全限定名_方法名 的格式(類全限定名中的 . 需要用 _ 替換)命名規(guī)范
extern "C" JNIEXPORT jstring JNICALL Java_com_louis_myjni_MainActivity_stringFromJNI( JNIEnv* env, //JNIEnv 指的是當前 JNI 環(huán)境,利用 JNIEnv 可以操作 java 層代碼 jobject /* this */) { //jobject 指的是 JNI 函數(shù)對應(yīng)的 java native 方法所在類的實例對象,如果 native 方法是 static 的話則類型就需要改成 jclass,代表的是類對象 std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }
動態(tài)注冊
- 當 Java 層調(diào)用 System.loadLibrary 方法時,JNI_OnLoad 函數(shù)會在庫被加載到 Java 虛擬機時調(diào)用
- PS:比如 Android 系統(tǒng)的 JNI 函數(shù)和 FFmpeg 相關(guān)函數(shù)和都是使用的動態(tài)注冊
//定義本地函數(shù),函數(shù)名任意 jstring stringFromJNI_JNI_OnLoad(JNIEnv *env, jobject/* this */) { std::string hello = "Hello from C++ stringFromJNI_JNI_OnLoad"; return env->NewStringUTF(hello.c_str()); } //定義一個 JNINativeMethod 結(jié)構(gòu)體數(shù)組(映射表),結(jié)構(gòu)體包含 Java 方法名、方法簽名(方法描述符)以及對應(yīng)關(guān)聯(lián)的本地函數(shù)的指針 JNINativeMethod nativeMethods[] = { //stringFromJNI 對應(yīng) Java native 方法,stringFromJNI_JNI_OnLoad 對應(yīng)本地函數(shù) {"stringFromJNI", "()Ljava/lang/String;", (jstring *) stringFromJNI_JNI_OnLoad} }; //Java 調(diào)用 System.loadLibrary 時此 JNI_OnLoad 函數(shù)就會執(zhí)行 //JavaVM *vm 參數(shù)是一個指向 Java 虛擬機的指針,通過這個指針可以訪問 JVM 的功能 //void *reserved 是一個保留參數(shù),通常不使用 JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env; //獲取與當前線程關(guān)聯(lián)的 JNI 環(huán)境 if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { LOGE("獲取 JNI 環(huán)境失敗"); return JNI_ERR; } //通過類的全限定名來查找并加載一個類,返回一個指向該類的 jclass 引用 jclass clazz = env->FindClass("com/louis/myjni/MainActivity"); if (clazz == NULL) { LOGE("獲取 jclass 失敗"); return JNI_ERR; } //計算 arr 數(shù)組的長度 int length = sizeof(arr) / sizeof(arr[0]); int nMethods = sizeof(nativeMethods) / sizeof(nativeMethods[0]); //參數(shù) nMethods 指定數(shù)組中 NativeMethod 的數(shù)量 if (env->RegisterNatives(clazz, nativeMethods, nMethods) != JNI_OK) { LOGE("注冊本地函數(shù)失敗"); return JNI_ERR; } LOGE("注冊成功"); return JNI_VERSION_1_6; //返回當前支持的 JNI 版本號 }
總結(jié)
靜態(tài)注冊簡單易用,函數(shù)必須要有類似 JNIEXPORT jstring JNICALL 這樣的相關(guān)聲明,而且函數(shù)名必須要按照 JNI 的命名規(guī)則去命名,由于 JNI 函數(shù)名暴露,所以存在一定的安全隱患
動態(tài)注冊靈活性高,不依賴于固定的命名規(guī)則,不過需要自行注冊手動關(guān)聯(lián),可以做到在運行時根據(jù)條件選擇不同的實現(xiàn),更適合復(fù)雜項目
到此這篇關(guān)于淺析Java中JNI靜態(tài)注冊和動態(tài)注冊的文章就介紹到這了,更多相關(guān)Java JNI靜態(tài)注冊和動態(tài)注冊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?synchronized同步關(guān)鍵字工作原理
synchronized作為Java程序員最常用同步工具,很多人卻對它的用法和實現(xiàn)原理一知半解,以至于還有不少人認為synchronized是重量級鎖,性能較差,盡量少用。但不可否認的是synchronized依然是并發(fā)首選工具,本文就來詳細講講2023-02-02Java Red5服務(wù)器實現(xiàn)流媒體視頻播放
這篇文章主要介紹了Java Red5服務(wù)器實現(xiàn)流媒體視頻播放,對視頻播放感興趣的同學,可以參考下2021-04-04Mybatis?resultMap標簽繼承、復(fù)用、嵌套方式
這篇文章主要介紹了Mybatis?resultMap標簽繼承、復(fù)用、嵌套方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03