深入浅出Android NDK之在jni中使用线程

目录
上一篇 深入浅出Android NDK之全局弱引用的使用

在jni中我们可以使用pthread或者std::thread创建线程。
因为线程并不是从Java环境创建的,所以这时候创建出的线程是没有JNIEnv的。
如果需要使用JNIEnv,可以调用JavaVM的AttachCurrentThread将当前线程附加到虚拟机。

jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)

AttachCurrentThread可以调用多次,第一次会附加当前线程到虚拟机并返回JNIEnv,之后再调用的时候因为当前线程已经被附加到虚拟机上了,所以就不需要重复附加了,仅仅只返回JNIEnv即可,作用相当于GetEnv。

需要注意的是,在线程退出之前我们必须要调用DetachCurrentThread从虚拟机分离当前线程,,不然会造成内存泄露,线程也不会退出。
对于native层创建出来的线程,在调用AttachCurrentThread的时候会创建本地引用表,在调用DetachCurrentThread的时候会释放本地引用表。
但是一般我们并不会频繁的调用AttachCurrentThread/DetachCurrentThread,这样效率很低。
一般我们在线程的入口函数调用一次AttachCurrentThread,在线程入口函数退出之前调用一次DetachCurrentThread即可。所以一定要手动释放每个本地引用。不然本地引用越来越多,很容易超出最大限制。
下面这个例子很好的演示了AttachCurrentThread/DetachCurrentThread的用法:

#include <jni.h>
#include <pthread.h>
#include <android/log.h>

extern JavaVM *g_vm;
JNIEnv* getEnv();

void* __start_routine(void*) {
    JNIEnv *env1, *env2, *env3, *env4, *env5;
    int ret;

    JavaVMAttachArgs args;
    args.version = JNI_VERSION_1_4;
    args.name = "pthread-test";//给线程起个名字吧,这样在调试或者崩溃的时候能显示出名字,而不是thead-1,thread-2这样的名字。
    args.group = NULL;//java.lang.ThreadGroup的全局引用,作用你懂的。

    //在调用AttachCurrentThread以前,是没有java环境的,所以GetEnv返回的JNIEnv是NULL
    g_vm->GetEnv((void**)&env1,JNI_VERSION_1_4);
    __android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "before AttachCurrentThread env is:%p", env1);

    //调用AttachCurrentThread,将当前线程附加到虚拟机,附加成功后,将会返回JNIEnv
    ret = g_vm->AttachCurrentThread(&env2, &args);
    __android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "do     AttachCurrentThread env is:%p, ret=%d", env2, ret);


    //在调用AttachCurrentThread以后,GetEnv返回了正确的JNIEnv
    g_vm->GetEnv((void**)&env3,JNI_VERSION_1_4);
    __android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "after  AttachCurrentThread env is:%p", env3);

    //再次调用AttachCurrentThread,直接返回JNIEnv,作用相当于GetEnv
    ret = g_vm->AttachCurrentThread(&env4, NULL);
    __android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "retry  AttachCurrentThread env is:%p, ret=%d", env4, ret);

    //从虚拟机分离线程
    g_vm->DetachCurrentThread();

    //在调用DetachCurrentThread以后,GetEnv返回NULL
    g_vm->GetEnv((void**)&env5,JNI_VERSION_1_4);
    __android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "after  DetachCurrentThread env is:%p", env5);
    return NULL;
}

extern "C" JNIEXPORT void Java_com_example_threadtest_PThread_start(JNIEnv *env, jclass clazz) {
    pthread_t thread;
    pthread_create(&thread, NULL, __start_routine, NULL);
}

下一篇 深入浅出Android NDK之崩溃分析

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值