NDK JNI JavaVM、JNIEnv和jobject的理解
创始人
2024-06-02 12:22:45

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() {@Overridepublic 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); // 提升全局引用

相关内容

热门资讯

苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...