JNI 接口回調
這裏主要演示從 Java 層傳入自定義listener,然後由 c/c++ 層在某一時期某一線程主動調用 listener 的回調函數,完成異步回調的功能。
關於 jni 的其他部分基礎知識本文不做詳細介紹。
Java 層定義代碼
-
java-native 函數以及接口定義
package com.jnidemo; public class Greet { static { System.loadLibrary("jni_demo"); } public native void native_say_hello(GreetListener listener); public interface GreetListener { /** * 這裏爲了演示自定義 Callback 的用法,使用了自定義 Java-Callback 類作爲回調參數, * 可直接使用基本類型或者其他引用類型做回調參數,根據自己的業務需求決定。 */ void onGreetReceive(GreetCallback greet); } }
-
java 自定義 Callback 類
package com.jnidemo; public class GreetCallback { private int greetID; private String greetMsg; public GreetCallback(int greetID, String greetMsg) { this.greetID = greetID; this.greetMsg = greetMsg; } }
c++ 層實現代碼
-
動態註冊 native 實現函數
const char *NATIVE_GREET_CLASS_NAME = "com/jnidemo/Greet"; static JNINativeMethod gMethods_Greet[] = { {"native_say_hello", "(Lcom/jnidemo/Greet$GreetListener;)V", (void *)say_hello_jni} }; static int registerNativeMethods(JNIEnv *env) { jclass clazz = env->FindClass(NATIVE_GREET_CLASS_NAME); if (clazz == NULL) return JNI_FALSE; if (env->RegisterNatives(clazz, gMethods_Greet, sizeof(gMethods_Greet) / sizeof(gMethods_Greet[0])) < 0) return JNI_FALSE; env->DeleteLocalRef(clazz); clazz = NULL; return JNI_TRUE; } jint JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env = NULL; if (vm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) return -1; assert(env != NULL); if (!registerNativeMethods(env)) return -1; hover_jvm = vm; return JNI_VERSION_1_6; }
-
native 函數實現
typedef struct { jobject o_greet_listener; jmethodID m_greet_receive; jclass z_greet_callback; jmethodID m_greet_callback_init; } jni_callback_t; // 定義承載 Java-Listener 和 Java-Callback 實例和 methodID 的結構體實例 static jni_callback_t *jni_callback; /** * jni 動態註冊對應的 native 實現函數。 * 創建 Java-Listener 和 Java-Callback 的實例和 methodID 並賦值給 jni_callback_t. */ static void say_hello_jni(JNIEnv *env, jobject object, jobject greet_listener) { if(!greet_listener) return; // 獲得回調接口 函數onGreetReceive 的 methodID. jclass clazz_greet_listener = env->GetObjectClass(greet_listener); jmethodID method_greet_receive = env->GetMethodID(clazz_greet_listener, "onGreetReceive", "(Lcom/jnidemo/GreetCallback;)V"); // 獲得自定義 Callback類 GreetCallback 構造函數的 methodID. jclass clazz_greet_callback = env->FindClass("com/jnidemo/GreetCallback"); jmethodID method_greet_callback_init = env->GetMethodID(clazz_greet_callback, "<init>", "(ILjava/lang/String;)V"); // 這裏創建 jni_callback_t 的實例,創建 Listener 和 Callback 的全局引用並賦值. jni_callback = (jni_callback_t *)malloc(sizeof(jni_callback_t)); memset(jni_callback, 0, sizeof(jni_callback_t)); jni_callback->o_greet_listener = env->NewGlobalRef(greet_listener); jni_callback->m_greet_receive = method_greet_receive; jni_callback->z_greet_callback = (jclass)env->NewGlobalRef(clazz_greet_callback); jni_callback->m_greet_callback_init = method_greet_callback_init; // 銷燬局部引用 env->DeleteLocalRef(clazz_greet_listener); env->DeleteLocalRef(clazz_greet_callback); clazz_greet_listener = NULL; clazz_greet_callback = NULL; // 這裏創建子線程模擬異步回調 pthread_t ntid; pthread_create(&ntid, NULL, async_thread_func, NULL); }
-
native 端子線程模擬回調
void *async_thread_func(void *args) { // JNI_OnLoad 時保存 jvm,這裏使用 jvm->AttachCurrentThread 獲取 JNIEnv,暫不做詳細介紹. JNIEnv *env = NULL; hover_attach_jni_env(&env); // 使用 java 構造函數生成 GreetCallback 的實例 int greetID = 1; jstring greetMsg = env->NewStringUTF("say hi to java."); jobject greet_callback = env->NewObject(jni_callback->z_greet_callback, jni_callback->m_greet_callback_init, greetID, greetMsg); // 調用 GreetListener 的 onGreetReceive 函數,完成調用流程. env->CallVoidMethod(jni_callback->o_greet_listener, jni_callback->m_greet_receive, greet_callback); // 銷燬局部引用 env->DeleteLocalRef(greetMsg); env->DeleteLocalRef(greet_callback); greetMsg = NULL; greet_callback = NULL; // 銷燬全局引用 --- 如果有多處或多次回調,自行判斷銷燬時機. env->DeleteGlobalRef(jni_callback->o_greet_listener); env->DeleteGlobalRef(jni_callback->z_greet_callback); jni_callback->o_greet_listener = NULL; jni_callback->z_greet_callback = NULL; free(jni_callback); jni_callback = NULL; return 0; }
Java 層調用 native 函數
new Greet().native_say_hello(new Greet.GreetListener() {
@Override
public void onGreetReceive(GreetCallback greet) {
Log.d(TAG, "hello, onGreetReceive: " + greet.toString());
}
});