前言
在執行Android JNI的時候或多或少都需要在JNI中去呼叫Java的函式.(不論是只有Android才能拿到的資料,或是需要做callback)
這裡就介紹如何在 JNI thread (或是 callback)中去呼叫 Java Method.
基礎觀念
先了解基礎概念,大部分而言會有兩種狀況需要由JNI呼叫Java.
- Java -> Jni -> 更新另外一個Java
- Jni的Callback or Thread -> 更新 Java
會需要這種狀況,大部分都是(2).當然也可能是(1).由於在(2)的狀況下限制比較多而且比較麻煩,所以只講解(2)的部分. (當然,一樣的方式也可以適合給(1)使用.
先講解可能的限制
由於我們是從jni裡面的thread 或是 jni 去呼叫C/C++ module的callback. 所以.. 你拿不到JNI Environment JNIEnv
跟原先呼叫或是你想要呼叫的Activity的jobject
.
所以這裡要先透過某個 JNI main thread的function.舉例而言,最基本的Hello World JNI 都會有個 stringFromJni()
透過那個main thread的JNI function.
相關流程
- 需要紀錄JVM (一般而言 JVM生命週期是每個App launch啟動)
- 也必須要記錄你要呼叫的 Activity 的 jobject
- 透過JVM取得目前的目前 JNIEnv (透過AttachCurrentThread)
- 依照一般流程呼叫
CallVoidMethod
->GetMethodID
->CallVoidMethod
(如果是呼叫void) - 透過DetachCurrentThread回收相關資源
程式碼:
容易出錯的部分:
錯誤的jobject使用
這邊主要是提醒,由於JNI function 函式裡面的參數 jobject thiz
的處理:
JNIEXPORT jstring Java_com_example_hellojni_stringFromJNI( JNIEnv *env, jobject thiz )
裡面的 JNIEnv *env
與 jobject thiz
主要解釋如下:
JNIEnv *env
: 負責處理JNI的環境,千萬注意在thread裡面的JNIEnv
會不同.所以要使用上面的AttachCurrentThreadjobject thiz
: 主要是處理該JNI library owner (Activity) 的Java Object.- 這裡要主義的是這個jobject 主要是
System.loadLibrary("hello-jni");
的Java Activity,而不是呼叫JNI function 的Java Object.
- 這裡要主義的是這個jobject 主要是
所以如果你有一個Java Class (ex:JNIJava)專門處理JNI,另外一個Java Class (ex: MainActivity)去建立 JNIJava然後去呼叫他,如果你需要呼叫回去MainActivity的函式,記得要把該Activity 傳進來. (不然就是要用FindClass找到正確的)
沒有使用AttachCurrentThread,造成CALL_TYPE
crash
如果在JNI native debugger 發現有CALL_TYPE
error 在 jni.h
.代表你再不是JNI main thread去使用main thread的JNIEnv
.所以要注意.