NDK JNI JavaVM、JNIEnv和jobject的理解

JavaVM、JNIEnv和jobject的理解

  1. 三者特点:

1)JavaVM:能够跨越线程,能够跨越函数;

2)JNIEnv:不能跨越线程,否则奔溃,可以跨越函数;

3)jobject:不能跨越线程,否则奔溃,不能跨越函数,否则奔溃。

  1. 代码验证

1)java层native函数定义、在主线程和子线程调用验证

/**
 * java层主线程调用
 */
public native void nativeFun0();
public native void nativeFun1();
/**
 * java层子线程调用
 */
public native void nativeFun2();

/**
 * 按钮点击事件
 */
public void clickMethod0(View view) {
    nativeFun0();
}

public void clickMethod(View view) {
    nativeFun1();
}

public void clickMethod2(View view) {
    new Thread() {
        @Override
        public void run() {
            super.run();
            nativeFun2(); // Java的子线程调用
        }
    }.start();
}

2)native层函数实现和native层函数子线程实现

// 定义全局javaVM,用于多线程使用
JavaVM *java_vm = nullptr;

// 重写JNI_OnLoad函数,在执行System.loadLibrary时,调用该函数,在此进行动态注册
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *javaVm, void *) {
    ::java_vm = javaVm;
    return JNI_VERSION_1_6;
}

/**
 * 在Java层主线程调用nativeFun0
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_ndk_thread_MainActivity_nativeFun0(JNIEnv *env, jobject thiz) {
    JavaVM *javaVm = nullptr;
    env->GetJavaVM(&javaVm);

    // 打印:当前函数env地址, 当前函数jvm地址, 当前函数job地址,  JNI_OnLoad的jvm地址
    LOGE("Java层主线程 nativeFun0 当前函数env地址%p,  当前函数jvm地址:%p,  当前函数job地址:%p, JNI_OnLoad的jvm地址:%p\n",
         env, javaVm, thiz, ::java_vm);
}

/**
 * 定义子线程的函数指针
 */
void *run(void *) {
    JNIEnv *jniEnv = nullptr;
    // 在子线程附件一个jniEnv
    ::java_vm->AttachCurrentThread(&jniEnv, nullptr);
    LOGE("native层子线程 run jvm地址:%p,  当前run函数的newEnv地址:%p \n", ::java_vm, jniEnv);
    // 释放
    ::java_vm->DetachCurrentThread();
    return nullptr;
}

/**
 * 在Java层主线程调用nativeFun1,native层调用子线程的函数指针
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_ndk_thread_MainActivity_nativeFun1(JNIEnv *env, jobject thiz) {
    JavaVM *javaVm = nullptr;
    env->GetJavaVM(&javaVm);
    // 打印:当前函数env地址, 当前函数jvm地址, 当前函数clazz地址,  JNI_OnLoad的jvm地址
    LOGE("Java层主线程 nativeFun1 当前函数env地址%p,  当前函数jvm地址:%p,  当前函数clazz地址:%p, JNI_OnLoad的jvm地址:%p\n",
         env,
         javaVm, thiz, ::java_vm);
    // 创建子线程
    pthread_t pid;
    pthread_create(&pid, nullptr, run, nullptr);
}

/**
 * 在Java层子线程调用nativeFun2
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_ndk_thread_MainActivity_nativeFun2(JNIEnv *env, jobject thiz) {
    JavaVM *javaVm = nullptr;
    env->GetJavaVM(&javaVm);

    // 打印:当前函数env地址, 当前函数jvm地址, 当前函数clazz地址,  JNI_OnLoad的jvm地址
    LOGE("Java层子线程 nativeFun2 当前函数env地址%p,  当前函数jvm地址:%p,  当前函数clazz地址:%p, JNI_OnLoad的jvm地址:%p\n",
         env,
         javaVm, thiz, ::java_vm);
}
  1. 小结

查看打印log分析

1)所有的JavaVM地址都是相同的,即JavaVM为同一个;

2)主线程的JNIEnv地址是相同,子线程的JNIEnv地址是不相同;

3)主线程的jobject地址是相同,子线程的jobject地址跟主线程是不相同;

结论:

1. JavaVM全局,绑定当前进程, 只有一个地址

2. JNIEnv线程绑定, 绑定主线程,主线程相同,故主线程地址相同;绑定子线程,多个子线程,故子线程地址不相同。

3. jobject 谁调用JNI函数,谁的实例会给jobject,如:MainActivity主线程调用地址指向MainActivity;子线程Thread程调用地址指向子线程Thread。

JNIEnv和jobject的崩溃解决:

JNIEnv:

使用全局的JavaVM附加当前异步线程 得到权限env操作。

JNIEnv *jniEnv = nullptr;
// 在子线程附件一个jniEnv
::java_vm->AttachCurrentThread(&jniEnv, nullptr);
// 释放
::java_vm->DetachCurrentThread();

jobject:

默认是局部引用,提升全局引用,可解决此问题。

env->NewGlobalRef(jobject); // 提升全局引用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sziitjin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值