最近在Android项目中要用到JNI,在C代码中,除了主线程外,还需要创建两个子线程,并且在子线程中回调了Java中的方法,因为对JNI模块不太熟悉,在开发过程中遇到了不少问题,今天主要记录下在子线程中回调Java中方法的问题。
刚开始,我的代码是这么写的,然后运行程序之后,就会崩溃:
JNIEnv* env;
void CallBackJavaMethod_T1(jobject obj, jstring str){
jclass clazz = (**env).FindClass(env, "com/example/jnidatapass/JNI");
jmethodID jmid = (**env).GetMethodID(env, clazz, "printStirng","(Ljava/lang/String;)V");
jobject jobj = (**env).AllocObject(env, clazz);
(**env).CallVoidMethod(env, jobj, jmid,str);
}
后来,在网上查询之后,才发现问题出在JNIEnv*变量上,JNIEnv*是和线程Thread对应的,每一个线程都有对应的JNIEnv对象,上面写的代码之所以报错,是因为在这个方法中,用到的JNIEnv*变量,是主线程中的,但这个函数是在子线程中调用的,所以,Exception。
修改后成功运行的代码:
void CallBackJavaMethod_T1(jobject obj, jstring str){
JNIEnv *env;
JavaVM *jvm;
int result, status;
status = jvm->GetEnv(jvm, (void**) &env, JNI_VERSION_1_2);
if (status < 0) {
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_2;
args.name = "user";
args.group = NULL;
result = (jvm)->AttachCurrentThread(jvm, (JNIEnv **) &env, &args);
if (result < 0) {
printError("event_dispatcher_log AttachCurrentThread failed");
}
}
jclass clazz = (**env).FindClass(env, "com/example/jnidatapass/JNI");
jmethodID jmid = (**env).GetMethodID(env, clazz, "printStirng",
"(Ljava/lang/String;)V");
jobject jobj = (**env).AllocObject(env, clazz);
(**env).CallVoidMethod(env, jobj, jmid, str);
if(status<0)
jvm->DetachCurrentThread(jvm);
}
如代码所示,在子线程调用的时候,获取了当前子线程的JNIEnv*对象,如果返回的是NULL,说明当前子线程还没有绑定JNIEnv*对象,需要通过AttachCurrentThread方法,传入一个JNIEnv*变量,之后就可以直接使用这个变量了。(在线程结束或者不在使用JNIEnv*的时候,需要调用DetachCurrentThread,解除绑定)。