遇到的問題,Native層本地多線程回調Java函數時env->findClass()失敗。
前面的代碼是這樣的在 JNI_OnLoad記錄全局變量g_vm static JavaVM* g_vm = NULL;
1 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) 2 3 { 4 5 JNIEnv * env = NULL; 6 7 if (g_vm == NULL) 8 { 9 g_vm = vm; 10 } 11 12 if (g_vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) 13 { 14 15 ALOGD("connot get g_Env is OK "); 16 17 return JNI_ERR; 18 19 } 20 21 return JNI_VERSION_1_4; 22 23 }
在子線程回調函數中去g_vm->AttachCurrentThread獲取env,通過env去findClass代碼如下這里發現cls == NULL
1 int nativeCallBackJava(int id) 2 3 { 4 5 jint result; 6 7 JNIEnv * env = NULL; 8 9 if (g_vm == NULL) 10 { 11 return FALSE; 12 } 13 14 result = (g_vm)->AttachCurrentThread(&env, NULL); 15 16 if (result != JNI_OK) 17 { 18 return FALSE; 19 } 20 21 cls = (env)->FindClass("com/lipeng/NativeCallJava"); 22 23 if (cls == NULL) 24 { 25 return FALSE; 26 } 27 28 mid = (env)->GetStaticMethodID(cls, "nativeNotifyJava", "(I)I"); 29 30 if (mid == NULL) 31 { 32 return FALSE; 33 } 34 35 (env)->CallStaticIntMethod(cls, mid, id); 36 37 return TRUE; 38 39 }
為什么會這樣呢?我並沒有看源碼,覺得子線程AttachCurrentThread得到的env其類的加載器中並沒有去加載自定義的類,所有這里你無法去findClass你自己的類,有人說這里find 系統類env->FindClass("java/lang/String");
這樣是可以的,我沒有做嘗試。
解決方案如下,
Java
1. 需要的類里面有個public native void setClsRef(void);
2. 在初始的時候調用本地函數來設計該類的全局引用
本地Native定義全局引用static jobject g_ObjCall = NULL;
實現setClsRef函數
3.
1 JNIEXPORT void JNICALL Java_com_lipeng_NativeCallJava_setClsRef(JNIEnv* env, jobject thiz) 2 3 { 4 5 if (g_ObjCall == NULL) 6 { 7 8 g_ObjCall = env->NewGlobalRef(thiz); //獲取全局引用 9 10 if (g_ObjCall == NULL) 11 12 { 13 14 ALOGD("get g_ObjCall == NULL) "); 15 16 } 17 18 if (thiz != NULL) 19 { 20 21 env->DeleteLocalRef(thiz); 22 }//釋放局部對象.這里可不要,調用結束后虛擬機會釋放 23 24 }
4.調用
1 int nativeCallBackJava(int id) 2 3 { 4 5 jint result; 6 7 JNIEnv * env = NULL; 8 9 if (g_vm == NULL) 10 { 11 return FALSE; 12 } 13 14 result = (g_vm)->AttachCurrentThread(&env, NULL); 15 16 if (result != JNI_OK) 17 { 18 return FALSE; 19 } 20 21 cls = (env)->GetObjectClass(g_ObjAd); //從全局引用獲取局部對象 22 23 if (cls == NULL) 24 { 25 return FALSE; 26 } 27 28 mid = (env)->GetStaticMethodID(cls, "nativeNotifyJava", "(I)I"); 29 30 if (mid == NULL) 31 { 32 return FALSE; 33 } 34 35 (env)->CallStaticIntMethod(cls, mid, id); 36 37 env->DeleteLocalRef(cls); //調用完后釋放局部對象,這里是需要的,子線程調用該局部對象不會被虛擬機銷毀,需顯示調用Delete 38 39 return TRUE; 40 41 }
這樣就可以在本地多線程隨意使用了,回調Java層了,子線程結束后別忘了 g_vm->DetachCurrentThread()