AndroidNDK開發系列教程6:JNI函式註冊(JNI_OnLoad)
在使用native方法前都會先載入該native方法的so檔案,通常在一個類的靜態程式碼塊中進行載入,當然也可以在建構函式,或者呼叫前載入。jvm在載入so時都會先呼叫so中的JNI_OnLoad函式,如果你沒有重寫該方法,那麼系統會給你自動生成一個。JNI_OnLoad方法的呼叫順序可以參考我的另一篇博文:JNI_OnLoad呼叫時機,下面我們可以在該方法中對自己的函式進行註冊。這就很爽了,jni預設的那個方法命名又臭又長,改的時候不注意還可能該錯。現在我們可以定義自己的函式名稱,只需要在JNI_OnLoad中註冊下對應的對映。在Google官網也有介紹:https://developer.android.com/training/articles/perf-jni.html
1. JNI_OnLoad簡介
在編寫JNI方法時有兩種方法:一種是標準的通過javah生成標頭檔案,然後自己實現對應的cpp檔案,這種辦法也是官方推薦的。還有一種方法是在JNI_OnLoad函式中進行函式對映,將java裡面的方法對映到自己實現的方法。
當Android的DVM(Virtual Machine)執行到C元件裡的System.loadLibrary()函式時,首先會去執行C元件裡的JNI_OnLoad()函式。它的用途有二:
1. 告訴VM此C元件使用那一個JNI版本。
如果你的*.so檔沒有提供JNI_OnLoad()函式,VM會預設該*.so檔是使用最老的JNI 1.1版本。
由於新版的JNI做了許多擴充,如果需要使用JNI的新版功能,
例如JNI 1.4的java.nio.ByteBuffer,就必須藉由JNI_OnLoad()函式來告知VM。
2. 由於VM執行到System.loadLibrary()函式時,就會立即先呼叫JNI_OnLoad(), 所以C元件的開發者可以藉由JNI_OnLoad()來進行C元件內的初期值之設定(Initialization) 。
3. 在so被成功解除安裝時,會回撥另一個JNI方法:JNI_UnOnLoad。這兩個方法宣告如下:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved);
其中第一個引數vm表示DVM虛擬機器,該vm在應用程式中僅有一個,可以儲存在native的靜態變數中,供其他函式或其他執行緒使用。其返回值表示當前需要native library需要的版本。
2. 舉個例子
首先在Java中寫好native方法:
//JNI_OnLoads使用例項
public native void jniOnLoadTest();
public native String jniOnload1(Person person);
然後編寫對應的native方法
//空方法可以不用傳任何欄位
//也可以傳這兩個引數:void onLoadTest(JNIEnv*env,jobject obj);兩個引數含義和用javah生成的一致。
void onLoadTest() {
LOGE("調到我啦");
}
//如果有引數,那麼需要加上前面兩個引數,不然會導致引數不對應。引數含義和javah生成的標頭檔案中引數含義一致。
jstring onloadTest1(JNIEnv *env, jobject instance, jobject obj) {
jclass pCls = env->GetObjectClass(obj);
jfieldID nameFid = env->GetFieldID(pCls, "name", "Ljava/lang/String;");
jstring name = (jstring) env->GetObjectField(obj, nameFid);
char *cname = jstringToChar(env, name);
char *tmp = new char[100];
sprintf(tmp, "我來自Native,我叫:%s", cname);
jstring result = charTojstring(env, tmp);
return result;
}
然後在JNI_OnLoad中註冊改函式對映
//註冊函式對映
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv *pEnv = NULL;
//獲取環境
jint ret = vm->GetEnv((void**) &pEnv, JNI_VERSION_1_6);
if (ret != JNI_OK) {
LOGE("jni_replace JVM ERROR:GetEnv");
return -1;
}
//在{}裡面進行方法對映編寫,第一個是java端方法名,第二個是方法簽名,第三個是c語言形式簽名(括號內表示方法返回值)
JNINativeMethod g_Methods[] = {{"jniOnLoadTest", "()V", (void*) onLoadTest},
{"jniOnload1", "(Lzqc/com/example/Person;)Ljava/lang/String;", (jstring*)onloadTest1}
};
jclass cls = pEnv->FindClass("zqc/com/example/NativeTest");
if (cls == NULL) {
LOGE("FindClass Error");
return -1;
}
//動態註冊本地方法
ret = pEnv->RegisterNatives(cls, g_Methods,sizeof(g_Methods) / sizeof(g_Methods[0]));
if (ret != JNI_OK) {
LOGE("Register Error");
return -1;
}
//返回java版本
return JNI_VERSION_1_6;
}
其中JNINativeMethod的結構如下:
typedef struct {
const char* name; // java層對應的方法名稱
const char* signature;// 該方法的返回值型別和引數型別
void* fnPtr; // native中對應的函式指標
} JNINativeMethod;
//註冊本地方法,第一個是方法對應的類,第二個是方法對映,第三個是對映方法的個數
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
jint nMethods)
{ return functions->RegisterNatives(this, clazz, methods, nMethods); }
通過以上方法就可以實現方法對映,而不用遵循原有的命名規則。
3. 總結
JNI_OnLoad是載入so時最先呼叫的方法,而且該方法會把JavaVM* vm指標傳過來,這樣在native就可以儲存該指標,該指標在整個應用程式中僅有一個,可以跨執行緒使用。我們通過在該方法中註冊函式對映,當然也可以在該方法中做其他操作。比如我們可以在該方法中進行版本校驗,也可以校驗當前呼叫該so的應用是否合乎要求。
相關文章
- AndroidNDK開發系列教程3:基本方法呼叫及傳參(續)Android
- Android Studio NDK開發:JNI呼叫Java函式AndroidJava函式
- JNI原始碼分析(並實現JNI動態註冊)原始碼
- JavaScript 註冊事件處理函式JavaScript事件函式
- linux核心cdev_init系列函式(字元裝置的註冊)Linuxdev函式字元
- Android JNI開發系列之配置Android
- JavaScript 批量註冊事件處理函式JavaScript事件函式
- 保姆教程系列二、Nacos實現註冊中心
- Swoole 回撥函式的註冊與呼叫函式
- for迴圈批量註冊事件處理函式事件函式
- js如何批量註冊事件處理函式JS事件函式
- SQLContext、HiveContext自定義函式註冊SQLContextHive函式
- Vue移動端系列 => [02-1] 登陸註冊的函式封裝Vue函式封裝
- Android 深入理解 JNI(一)JNI 原理與靜態、動態註冊Android
- ChatGPT最詳細註冊教程+不註冊直接使用教程ChatGPT
- 前端開發--登陸註冊前端
- ffmpeg分析系列之一(註冊該註冊的)
- ECMAScript 6教程 (二) 物件和函式物件函式
- javascript如何移除註冊的事件處理函式JavaScript事件函式
- attachEvent()註冊事件處理函式this指向問題事件函式
- js刪除註冊的事件處理函式JS事件函式
- Spark註冊UDF函式,用於DataFrame DSL or SQLSpark函式SQL
- C# 註冊並使用sqlite 自定義函式C#SQLite函式
- 在c中,怎麼註冊回撥函式函式
- Navicat 16 註冊教程
- web前端開發教程:函式是什麼Web前端函式
- JNI/NDK開發指南(1):JNI開發流程及HelloWorld
- ES6 系列之箭頭函式函式
- Jni函式的靜態繫結函式
- 直播app開發搭建,註冊頁面樣式,全部程式碼APP
- JNI開發流程
- jQuery如何解綁註冊的事件處理函式jQuery事件函式
- 如何為新增的元素註冊事件處理函式事件函式
- IDAPython指令碼分享 - 自動在JNI_OnLoad下斷點Python指令碼斷點
- Python開發的入門教程(六)-函式Python函式
- MVC之前的那點事兒系列(6):動態註冊HttpModuleMVCHTTP
- Chatgpt註冊全流程教程ChatGPT
- Android JNI開發系列之Java與C相互呼叫AndroidJava