如何在JNI中使用线程
答案:2 悬赏:70 手机版
解决时间 2021-03-16 00:01
- 提问者网友:原来太熟悉了会陌生
- 2021-03-15 05:57
如何在JNI中使用线程
最佳答案
- 五星知识达人网友:神鬼未生
- 2021-03-15 06:07
JNIEnv* g_env;
jobject g_thiz;
JavaVM *g_jvm;
void* thread_get_str(void * argv) {
(g_jvm)->AttachCurrentThread(&g_env, NULL);
LOGE("log in another thread!!!!!!!!!!");
jclass clazz = (g_env)->GetObjectClass(g_thiz);
jfieldID fid = (g_env)->GetFieldID(clazz, "mInstanceName", "Ljava/lang/String;");
jstring jstr = (jstring)(g_env)->GetObjectField(g_thiz, fid);
LOGE("this message is from other thread of c++ %s", (g_env)->GetStringUTFChars(jstr, NULL));
(g_jvm)->DetachCurrentThread();
}
void threadTest(JNIEnv* env, jobject thiz) {
if (g_thiz) {
(g_env)->DeleteGlobalRef(g_thiz);
}
g_thiz = (env)->NewGlobalRef(thiz);
g_env = env;
pthread_t thread;
pthread_create(&thread, NULL, thread_get_str, NULL);
}
看到了么关键是(g_jvm)->AttachCurrentThread(&g_env, NULL); 和(g_jvm)->DetachCurrentThread(); 函数,是把当前的线程绑定到jvm上,我们通过全局变量实现了参数的传递,当然了,其实我们也可以调用AndroidRuntime::getRuntime来获得vm但是比较麻烦,因为有些头文件的配置比较麻烦。至此,我们成功的在c++层使用了线程。
尽管现在中国的工程师还是比较急功近利,总是期望知道是什么,而很少问为什么,但是我觉得一个优秀的工程师应该知道为什么。
首先为什么我们不调用那两个函数就无法使用线程。我的功力有限,简单的看了下,我们JNI函数中的,evn其实是当前线程的环境参数,使用当我们创建新的线程就无法使用了,当我们调用AttachCurrentThread就是为当前线程创建一些环境参数。
1,AttachCurrentThread 调用 attachThread
2, JavaVMAttachArgs argsCopy;
if (args == NULL) {
argsCopy.version = JNI_VERSION_1_2;
argsCopy.name = NULL;
argsCopy.group = (jobject) dvmGetMainThreadGroup();
} else {
assert(args->version >= JNI_VERSION_1_2);
argsCopy.version = args->version;
argsCopy.name = args->name;
if (args->group != NULL) {
argsCopy.group = (jobject) dvmDecodeIndirectRef(NULL, args->group);
} else {
argsCopy.group = (jobject) dvmGetMainThreadGroup();
}
}
bool result = dvmAttachCurrentThread(&argsCopy, isDaemon);
这里我们copy了当期的环境参数到新的线程中。
然后接下来就创建线程,然后让线程跑起来。
jobject g_thiz;
JavaVM *g_jvm;
void* thread_get_str(void * argv) {
(g_jvm)->AttachCurrentThread(&g_env, NULL);
LOGE("log in another thread!!!!!!!!!!");
jclass clazz = (g_env)->GetObjectClass(g_thiz);
jfieldID fid = (g_env)->GetFieldID(clazz, "mInstanceName", "Ljava/lang/String;");
jstring jstr = (jstring)(g_env)->GetObjectField(g_thiz, fid);
LOGE("this message is from other thread of c++ %s", (g_env)->GetStringUTFChars(jstr, NULL));
(g_jvm)->DetachCurrentThread();
}
void threadTest(JNIEnv* env, jobject thiz) {
if (g_thiz) {
(g_env)->DeleteGlobalRef(g_thiz);
}
g_thiz = (env)->NewGlobalRef(thiz);
g_env = env;
pthread_t thread;
pthread_create(&thread, NULL, thread_get_str, NULL);
}
看到了么关键是(g_jvm)->AttachCurrentThread(&g_env, NULL); 和(g_jvm)->DetachCurrentThread(); 函数,是把当前的线程绑定到jvm上,我们通过全局变量实现了参数的传递,当然了,其实我们也可以调用AndroidRuntime::getRuntime来获得vm但是比较麻烦,因为有些头文件的配置比较麻烦。至此,我们成功的在c++层使用了线程。
尽管现在中国的工程师还是比较急功近利,总是期望知道是什么,而很少问为什么,但是我觉得一个优秀的工程师应该知道为什么。
首先为什么我们不调用那两个函数就无法使用线程。我的功力有限,简单的看了下,我们JNI函数中的,evn其实是当前线程的环境参数,使用当我们创建新的线程就无法使用了,当我们调用AttachCurrentThread就是为当前线程创建一些环境参数。
1,AttachCurrentThread 调用 attachThread
2, JavaVMAttachArgs argsCopy;
if (args == NULL) {
argsCopy.version = JNI_VERSION_1_2;
argsCopy.name = NULL;
argsCopy.group = (jobject) dvmGetMainThreadGroup();
} else {
assert(args->version >= JNI_VERSION_1_2);
argsCopy.version = args->version;
argsCopy.name = args->name;
if (args->group != NULL) {
argsCopy.group = (jobject) dvmDecodeIndirectRef(NULL, args->group);
} else {
argsCopy.group = (jobject) dvmGetMainThreadGroup();
}
}
bool result = dvmAttachCurrentThread(&argsCopy, isDaemon);
这里我们copy了当期的环境参数到新的线程中。
然后接下来就创建线程,然后让线程跑起来。
全部回答
- 1楼网友:人類模型
- 2021-03-15 07:19
如果你想了解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方法。
我要举报
如以上问答信息为低俗、色情、不良、暴力、侵权、涉及违法等信息,可以点下面链接进行举报!
大家都在看
推荐资讯