如何在JNI中使用线程

好一阵子没有研究技术了,项目管理占据了我很多时间,今天抽空研究了下JNI下面如何创建线程。
我们知道java层创建线程是很容易的事情,继承Thread或者实现Runnable接口。
有很多事情我们必须在c++层使用,这个就用到JNI,我们可能会使用网络,这个时候我们可能开启线程,最后回调java层的方法。那如何在JNI层使用线程呢?
我们知道pthread_create可以在linux下面创建一个线程,很可惜如果光这样做是无法在android平台使用的,因为这个线程不归dalvik管理,使用我们的虚拟机就会死掉。
会报下面的错误。

05-07 09:03:25.673: E/dalvikvm(718): JNI ERROR: non-VM thread making JNI calls
那怎么办呢?

上代码
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) {
        /* allow the v1.1 calling convention */
        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了当期的环境参数到新的线程中。
然后接下来就创建线程,然后让线程跑起来。

这里我们留一个疑问,也是我的疑问,我们的linux是如何创建线程的,据我所知,linux内核其实是无法区分进程和线程的,对于内核来说一个进程的线程不过是共享线性地址的多个内核线程。只有库函数是如何安排的,我确实没有研究过,使用也无法理解dalvik是如何把虚拟机和c++线程联系起来的。有知道的朋友希望不吝赐教。
posted @ 2013-07-29 17:33 samxu 阅读(...) 评论(...)  编辑 收藏