[Android JNI] --- JNIEnv和JavaVM

news/2024/2/29 2:35:03

1 JVMEnv

1.1 JNIEnv 是什么

JNIEnv 即 Java Native Interface Environment,Java 本地编程接口环境。JNIEnv 内部定义了很多函数用于简化我们的 JNI 编程。
JNIEnv是提供JNI Native函数的基础环境,线程相关,不同线程的JNIEnv相互独立,并且JNIEnv是一个JNI接口指针,指向了本地方法的一个函数表,该函数表中的每一个成员指向了一个JNI函数,本地方法通过JNI函数来访问JVM中的数据结构,详情如下图:
在这里插入图片描述
通过上面的图示,我们应该更加了解JNIEnv只在当前线程中有效。本地方法不 能将JNIEnv从一个线程传递到另一个线程中。相同的 Java 线程中对本地方法多次调用时,传递给该本地方法的JNIEnv是相同的。但是,一个本地方法可被不同的 Java 线程所调用,因此可以接受不同的 JNIEnv。
在 C++ 代码中,JNIEnv 是一个 JNIEnv_ 结构体:

#ifdef __cplusplus
typedef JNIEnv_ JNIEnv;
#else
typedef const struct JNINativeInterface_ *JNIEnv; 
#endifstruct JNIEnv_ {const struct JNINativeInterface_ *functions;
#ifdef __cplusplusjint GetVersion() {return functions->GetVersion(this);}jclass DefineClass(const char *name, jobject loader, const jbyte *buf,jsize len) {return functions->DefineClass(this, name, loader, buf, len);}jclass FindClass(const char *name) {return functions->FindClass(this, name);}jmethodID FromReflectedMethod(jobject method) {return functions->FromReflectedMethod(this,method);}jfieldID FromReflectedField(jobject field) {return functions->FromReflectedField(this,field);}jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) {return functions->ToReflectedMethod(this, cls, methodID, isStatic);}jclass GetSuperclass(jclass sub) {return functions->GetSuperclass(this, sub);}//省略其他函数//......
}

1.2 如何获取到JNIEnv

对于单线程的情况,我们可以直接通过 JNI 方法传入的参数获取到 JNIEnv,所有的Native函数的第一个参数永远是JNIEnv指针

// 第一个参数就是 JNIEnv
JNIEXPORT jstring JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject obj)
{return (*env)->NewStringUTF(env,"Hello from JNI !");
}

对于多线程的情况,首先我们要知道,JNIEnv 是一个线程作用域的变量,不能跨线程传递,不同线程的 JNIEnv 彼此独立。那如何在不同的线程中获取到 JNIEnv :

//定义全局变量
//JavaVM 是一个结构体,用于描述 Java 虚拟机,后面会讲
JavaVM* gJavaVM;JNIEXPORT jstring JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject obj)
{   //线程不允许共用env环境变量,但是JavaVM指针是整个jvm共用的,所以可以通过下面的方法保存JavaVM指针,在线程中使用env->GetJavaVM(&gJavaVM);return (*env)->NewStringUTF(env,"Hello from JNI !");
}//假设这是一个工具函数,可能被多个线程调用
void util_xxx()
{JNIEnv *env;//从全局的JavaVM中获取到环境变量gJavaVM->AttachCurrentThread(&env,NULL);//就可以使用 JNIEnv 了//最后需要做清理操作gJavaVM->DetachCurrentThread();
}

1.3 JVIEnv内部函数分类

JNIEnv 中定义的函数可以分为以下几类:

函数名功能
FindClass用于获取类
GetObjectClass通过对象获取这个类
NewGlobalRef创建 obj 参数所引用对象的新全局引用
NewObject构造新 Java 对象
NewString利用 Unicode 字符数组构造新的 java.lang.String 对象
NewStringUTF利用 UTF-8 字符数组构造新的 java.lang.String 对象
NewArray创建类型为Type的数组对象
GetField获取类型为Type的字段
SetField设置类型为Type的字段的值
GetStaticField获取类型为Type的static的字段
SetStaticField设置类型为Type的static的字段的值
CallMethod调用返回类型为Type的方法
CallStaticMethod调用返回值类型为Type的static方法

相关的函数不止上面的这些,这些函数的介绍和使用方法,我们可以在开发过程中参考官方文档
https://docs.oracle.com/en/java/javase/11/docs/specs/jni/index.html

2 JavaVM

JavaVM,英文全称是Java virtual machine,就是Java虚拟机。一个JVM中只有一个JavaVM对象,这个JavaVM则可以在进程中的各线程间共享的,这个特性在JNI开发中是非常重要的。

2.1 JavaVM定义

JavaVM申明在jni.h文件里面,因为我们在JNI开发中,必定要引入#include <jni.h>头文件。
C语言中JavaVM声明如下:

struct _JNIEnv;
struct _JavaVM;
typedef const struct JNINativeInterface* C_JNIEnv;#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;//C语言定义
#endif/** JNI invocation interface.*/
struct JNIInvokeInterface {void*       reserved0;void*       reserved1;void*       reserved2;jint        (*DestroyJavaVM)(JavaVM*);jint        (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);jint        (*DetachCurrentThread)(JavaVM*);jint        (*GetEnv)(JavaVM*, void**, jint);jint        (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);
};

C++中JavaVM声明如下:

struct _JNIEnv;
struct _JavaVM;
typedef const struct JNINativeInterface* C_JNIEnv;#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif/** C++ version.*/
struct _JavaVM {const struct JNIInvokeInterface* functions;#if defined(__cplusplus)jint DestroyJavaVM(){ return functions->DestroyJavaVM(this); }jint AttachCurrentThread(JNIEnv** p_env, void* thr_args){ return functions->AttachCurrentThread(this, p_env, thr_args); }jint DetachCurrentThread(){ return functions->DetachCurrentThread(this); }jint GetEnv(void** env, jint version){ return functions->GetEnv(this, env, version); }jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args){ return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }
#endif /*__cplusplus*/
};

2.2 获取JavaVM

方式一:
在加载动态链接库的时候,JVM会调用JNI_OnLoad(JavaVM* jvm, void* reserved)(如果定义了该函数)。第一个参数会传入JavaVM指针。代码如下:

jint JNI_OnLoad(JavaVM * vm, void * reserved){JNIEnv * env = NULL;jint result = -1;if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {LOGE(TAG, "ERROR: GetEnv failed\n");goto bail;}result = JNI_VERSION_1_4;bail:return result;

方式二:通过JNIEnv获取JavaVM,具体参考代码如下:

JNIEXPORT void JNICALL Java_com_xxx_android2native_JniManager_openJni(JNIEnv * env, jobject object)
{LOGE(TAG, "Java_com_xxx_android2native_JniManager_openJni");//注意,直接通过定义全局的JNIEnv和Object变量,在此保存env和object的值是不可以在线程中使用的//线程不允许共用env环境变量,但是JavaVM指针是整个jvm共用的,所以可以通过下面//的方法保存JavaVM指针,在线程中使用env->GetJavaVM(&gJavaVM);//同理,jobject变量也不允许在线程中共用,因此需要创建全局的jobject对象在线程//中访问该对象gJavaObj = env->NewGlobalRef(object);gIsThreadStop = 0;

3 ava和Android中JavaVM对象有区别

在Java里,每一个Process可以产生多个JavaVM对象,但是在Android上,每一个Process只有一个art虚拟机对象,也就是在Android进程中是通过有且只有一个虚拟器对象来服务所有Java和C/C++代码 。Java 的dex字节码和C/C++的*.so同时运行ART虚拟机之内,共同使用一个进程空间。之所以可以相互调用,也是因为有ART虚拟机。当Java 代码需要C/C++代码时,在ART虚拟机加载进*.so库时,会先调用JNI_Onload(),此时就会把JAVA VM对象的指针存储于c层jni组件的全局环境中,在Java层调用C层的本地函数时,调用C本地函数的线程必然通过ART虚拟机来调用C层的本地函数,此时,ART虚拟机会为本地的C组件实例化一个JNIEnv指针,该指针指向ART虚拟机的具体的函数列表,当JNI的c组件调用Java层的方法或者属性时,需要通过JNIEnv指针来进行调用。 当本地C/C++想获得当前线程所要使用的JNIEnv时,可以使用ART虚拟机对象的JavaVM* jvm->GetEnv()返回当前线程所在的JNIEnv*。


http://www.ppmy.cn/news/751338.html

相关文章

小米10 信息

wifi6 最大吞吐量9.6Gbps&#xff0c;比wifi5提升2.7倍&#xff0c;wifi4提升16倍TWT技术&#xff0c;通知设备不使用wifi的时候wifi模块休眠和唤醒8x8 mu-mimo&#xff1a;OFDMA&#xff1a; HIFI 光影照片最考验镜头 超广角 人像&#xff1a;肤色 逆光人像 夜景人像 前置…

小米10如何安装google play商店

查了一下网上说可以安装gmail, 小米商店就会自动安装google play的&#xff0c; 但是发现gmail在小米商店已经提示说“因为软件本身问题不能给安装"&#xff0c; 查了一无果&#xff0c;于是用之前华为安装google的apk来进行安装. https://download.csdn.net/download/…

小米10至尊纪念版和小米10pro 的区别 哪个好

小米10至尊纪念版和小米10Pro都采用了一块6.67英寸的曲面打孔屏&#xff0c;区别在于机身背部&#xff0c;小米10至尊纪念版有透明版、陶瓷黑和亮银版三种设计&#xff0c;而小米10Pro只有珍珠白和星空蓝两种配色设计。 小米10至尊纪念版更多使用感受和评价&#xff1a;http://…

小米10pro和小米10的区别

小米10和小米10Pro或将都采用目前主流的挖孔屏设计&#xff0c;前置摄像头开孔位于屏幕左上角。 小米10爆降800这活动太给力了 机会不容错过 https://www.xiaomi.com/10 不同的是&#xff0c;小米10搭载的是三星直板全面屏&#xff0c;屏幕刷新率达到90Hz&#xff0c;支持MEMC动…

小米10至尊纪念版和iPhone12哪个好 小米10至尊纪念版和iPhone12参数对比

小米10至尊纪念版在小米10Pro的90Hz刷新率的基础上&#xff0c;进一步提升至120Hz刷新率&#xff0c;同时屏幕素质还比较接近。 小米10至尊纪念版更多使用感受和评价&#xff1a;https://www.xiaomi.com/10 iphone11 爆降2000 太给力了&#xff1a;https://www.apple.com.cn 而…

小米颇为无奈,旧款的小米10比新款旗舰小米11更受欢迎

据某电商3月份的热销手机排行榜数据显示&#xff0c;已发布一年多时间的小米10的销量竟然在小米11之上&#xff0c;显示出小米11这款手机虽然被小米热推&#xff0c;但是消费者却更欢迎旧款的小米10。 据某电商的这份销量排名数据&#xff0c;iPhone11高居销量榜首&#xff0c;…

小米10 twrp刷入开发版

刷机有风险&#xff0c;解锁保平安 1.解锁 2. 下载想要刷的开发版卡刷包.zip放到手机内存目录下面 3. 刷入第三方recovery 首先对手机进行文件备份&#xff0c;因为卡刷会清除数据&#xff1b;然后刷入wzsx150大佬制作的recovery链接提取码&#xff1a;vrlq &#xff08;该…

小米10 MIUI 12 Magisk root教程(无需刷REC)

本教程适用于小米10的开发版、稳定版&#xff0c;无需刷REC&#xff0c;即可实现Root 本文基于小米10 MIUI 12开发版&#xff0c;20.12.10版本&#xff0c;其他版本过程相同 准备条件&#xff1a; 下面的工具可打包下载&#xff1a;https://pan.baidu.com/s/1GfkAZWYqGVnefZcf0…

小米10和小米10pro的区别 哪个更值得入手

小米10全系采用的是AMOLED曲面小挖孔屏&#xff0c;分辨率为1080P&#xff0c;并且支持90Hz刷新率&#xff0c;及180采样率&#xff0c;支持HDR10&#xff0c;DCI-P3的广色域&#xff0c;这些参数都是一致的。 小米10更多使用感受和评价&#xff1a;https://www.xiaomi.com/10 …

小米10 MIUI13升级/安卓12刷入TWRP/面具/Root/ 教程

文章教程&#xff1a; https://www.bilibili.com/read/cv14974449/ 小米10 TWRP下载&#xff1a; http://www.romleyuan.com/lec/read?id336 https://pan.baidu.com/s/1K0VuCxwwKT9OI3yCouV3Gg 完成后重启手机&#xff0c;打开magisk提示需要升级&#xff0c;删除&#xf…

小米10 Ultra从MIUI 13.0.3降级到12.0.15

用了一段时间MIUI 13&#xff0c;感觉在相机这一块砍得太深&#xff0c;成像太模糊了&#xff0c;所以想换回老版本玩玩。 在小米论坛逛了一圈&#xff0c;大家都是在刷12.0.10和12.0.15这两个版本&#xff0c;不过12.0.10只有卡刷包&#xff0c;没有提供线刷包&#xff0c;偷…

因泄露小米10至尊版被判赔100万元

本文转载自站长之家 6月24日消息&#xff0c;数码博主“Beautiful科技”在微博发布道歉信&#xff0c;称自己因2020年8月11日将小米10至尊纪念版开箱视频提前发出&#xff0c;违反了与小米公司的保密协议。经由中国国际经济贸易仲裁委仲裁&#xff0c;被判赔偿小米公司100万元…

QT6在线下载安装慢的问题

由于某“墙”的原因&#xff0c;在国内安装QT是会要了老命的&#xff0c;下载只有几十K&#xff0c;安装QT6保守估计得按天计算了。 经过多次尝试&#xff0c;终于找到了可以“几十MB”速度下载安装的办法。 方法一&#xff1a; qt-unified-windows-x64-4.5.2-online.exe --…

600条最强Linux命令总结

今天&#xff0c;带来一篇 Linux 命令总结的非常全的文章&#xff0c;也是我们平时工作中使用率非常高的操作命令&#xff0c;命令有点多&#xff0c;建议小伙伴们可以先收藏后阅读。 1. 基本命令 uname -m 显示机器的处理器架构 uname -r 显示正在使用的内核版本 dmidecode …

Mac ZeroTire 的重启方式

关闭 bash sudo launchctl unload /Library/LaunchDaemons/com.zerotier.one.plist 开启 bash sudo launchctl load /Library/LaunchDaemons/com.zerotier.one.plist

Mac 重启后自动启动 docker container

docker container update --restartalways 327916827cb7327916827cb7 为需要重启的 docker container 的 sha1 号

关于mac重启/home目录丢失解决方案

首先vim /etc/auto_master查看home所在行是否被注释&#xff0c;如果没被注释&#xff0c;百度如何关sip&#xff0c;自己创建。 如果被注释了&#xff0c;sudo vim /etc/auto_master把注释去掉&#xff0c;然后去根目录下执行sudo automount -vc 然后home目录就回来了&#x…

关于 idea 存在情况下的mac 自动重启 解决方式

本人电脑型号&#xff1a;2018 mbp 13.3 macos 10.15.6 idea 在mac上 导致mac 频繁关机重启解决方式 当然是你去过售后 或者你自认为你的电脑的硬件是完全没有问题的情况下这样做。才可以。 不用听 apple售后的动不动就格式化重新安装系统。屁用没有&#xff01;&#xff01;…

Mac下php-fpm重启方式

1、查看php-fpm端口使用情况 sudo lsof -i:9000 2、修改php.ini文件使其生效&#xff0c;需要重启php-fpm&#xff0c;关闭php-fpm命令 sudo killall php-fpm 3、启动php-fpm sudo php-fpm

Linux or Mac 重启网络

Mac sudo ifconfig en0 down sudo ifconfig en0 up Linux /etc/init.d/networking restart 转载于:https://www.cnblogs.com/daryl-blog/p/11369569.html
最新文章