如何在多线程中使用JNI?



  • 如果你想了解JNI在如何在多线程下使用
  • 如果你在子线程使用JNI时遇到findClass不能找到目标Class,而在主线程下却能找到该Class的问题。或是GetEnv返回NULL的问题
  • 如果你想多学点编程技术的话
那么,这篇文章就是为你而写的, :)

最近工作中遇到这么个问题:c++代码需要调用Android的API来做一个比较耗时的任务,因为有点耗时,希望能有个进度条显示给用户,很自然地,我创建了一个子线程用来执行这个耗时的任务,按照平时写法,结果一运行,GetEnv获取失败了。网上查找一番,官方说明有这么句话:
If the current thread is not attached to the VM, sets *env to NULL, and returns JNI_EDETACHED. If the specified version is not supported, sets *env to NULL, and returns JNI_EVERSION. Otherwise, sets *env to the appropriate interface, and returns JNI_OK.

调试后找到了原因,the current thread is not attached。 原来子线程函数里需要使用AttachCurrentThread()和DetachCurrentThread()这两个函数。没错,你需要gJvm->AttachCurrentThread(&env, NULL);来获取env,这样写之后,以为万事大吉了,结果findClass出错了,没有找到目标类,可是我千真万确地记得在主线程里这么写是没有问题的。env没有问题了,这回又哪里出错了呢?上网google一番,噢,不对,google被墙了,是用bing查找一番后,总算有些眉目了。

首先确保你的class name写对了,以包名开头,并用反斜杠隔开。如果class name没有错,那么应该是class loader的问题了。解决方法是你先在主线程中获取该class,并且将其保存为全局变量,以便其他线程使用。
jclass tmp = env->FindClass("com/example/company/MyClass");  
myClass = (jclass)env->NewGlobalRef(tmp);

在子线程中,
mid = env->GetStaticMethodID(cls, "fromJNI", "(I)V");
if (mid != NULL)
{
    env->CallStaticVoidMethod(env, cls, mid, i);
}
当然,也有其他解决方法,至少我使用这种方法成功了。而接下来在java中调用c++的代码就比较顺利了,木有碰到问题了。

总结:
1.在JNI_OnLoad中,保存JavaVM*,这是跨线程的,持久有效的,而JNIEnv*则是当前线程有效的。一旦启动线程,用AttachCurrentThread方法获得env。
2.通过JavaVM*和JNIEnv可以查找到jclass。
3.把jclass转成全局引用,使其跨线程。
4.然后就可以正常地调用你想调用的方法了。
5.用完后,别忘了delete掉创建的全局引用和调用DetachCurrentThread方法。


如何在多线程中使用JNI?,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。