JNI 编程上手指南之多线程

核心要点

JNI 环境下,进行多线程编程,有以下两点是需明确的:

  • JNIEnv 是一个线程作用域的变量,不能跨线程传递,每个线程都有自己的 JNIEnv 且彼此独立
  • 局部引用不能在本地函数中跨函数使用,不能跨线前使用,当然也不能直接缓存起来使用

示例程序

示例程序主要演示:

  • 如何在子线程获取到属于子线程自己的 JNIEnv
  • 上面说了局部引用不能再线程之间直接传递,所以我们只有另觅他法。

Java 层:

public void javaCallback(int count) {
    Log.e(TAG, "onNativeCallBack : " + count);
}

public native void threadTest();

Native 层:

static int count = 0;
JavaVM *gJavaVM = NULL;//全局 JavaVM 变量
jobject gJavaObj = NULL;//全局 Jobject 变量
jmethodID nativeCallback = NULL;//全局的方法ID

//这里通过标志位来确定 两个线程的工作都完成了再执行 DeleteGlobalRef
//当然也可以通过加锁实现
bool main_finished = false;
bool background_finished = false;

static void *native_thread_exec(void *arg) {

    LOGE(TAG, "nativeThreadExec");
    LOGE(TAG, "The pthread id : %d\n", pthread_self());
    JNIEnv *env;
    //从全局的JavaVM中获取到环境变量
    gJavaVM->AttachCurrentThread(&env, NULL);

    //线程循环
    for (int i = 0; i < 5; i++) {
        usleep(2);
        //跨线程回调Java层函数
        env->CallVoidMethod(gJavaObj, nativeCallback, count++);
    }
    gJavaVM->DetachCurrentThread();

    background_finished = true;

    if (main_finished && background_finished) {
        env->DeleteGlobalRef(gJavaObj);
        LOGE(TAG, "全局引用在子线程销毁");
    }

    return ((void *) 0);

}


extern "C"
JNIEXPORT void JNICALL
Java_com_yuandaima_myjnidemo_MainActivity_threadTest(JNIEnv *env, jobject thiz) {
    //创建全局引用,方便其他函数或线程使用
    gJavaObj = env->NewGlobalRef(thiz);
    jclass clazz = env->GetObjectClass(thiz);
    nativeCallback = env->GetMethodID(clazz, "javaCallback", "(I)V");
    //保存全局 JavaVM,注意 JavaVM 不是 JNI 引用类型
    env->GetJavaVM(&gJavaVM);

    pthread_t id;
    if (pthread_create(&id, NULL, native_thread_exec, NULL) != 0) {
        return;
    }

    for (int i = 0; i < 5; i++) {
        usleep(20);
        //跨线程回调Java层函数
        env->CallVoidMethod(gJavaObj, nativeCallback, count++);
    }

    main_finished = true;

    if (main_finished && background_finished && !env->IsSameObject(gJavaObj, NULL)) {
        env->DeleteGlobalRef(gJavaObj);
        LOGE(TAG, "全局引用在主线程销毁");
    }
}

示例代码中,我们的子线程需要使用主线程中的 jobject thiz,该变量是一个局部引用,不能赋值给一个全局变量然后跨线程跨函数使用,我们通过 NewGlobalRef 将局部引用装换为全局引用并保存在全局变量 jobject gJavaObj 中,在使用完成后我们需要使用 DeleteGlobalRef 来释放全局引用,因为多个线程执行顺序的不确定性,我们使用了标志位来确保两个线程所有的工作完成后再执行释放操作。

JNIEnv 是一个线程作用域的变量,不能跨线程传递,每个线程都有自己的 JNIEnv 且彼此独立,实际开发中,我们通过以下代码:

JavaVM *gJavaVM = NULL;
//主线程获取到 JavaVM
env->GetJavaVM(&gJavaVM);

//子线程通过 JavaVM 获取到自己的 JNIEnv
JNIEnv *env;
gJavaVM->AttachCurrentThread(&env, NULL);

在子线程中获取到 JNIEnv。JavaVM 是一个普通指针,由 JVM 来管理其内存的分配与回收,不是 JNI 引用类型,所以
我们可以把它赋值给一个全局变量,直接用,也不用考虑他的内存分配与后手问题。

阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/20412,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?