NDK(三):JNIEnv解析
文章目录
- 一、概述
- 二、JNIEnv结构体
- 三、JNINativeInterface结构体
- 3.1 Class操作
- 3.2 反射操作
- 3.3 对象字段 & 方法操作
- 3.4 类的静态字段 & 静态方法操作
- 3.5 字符串操作
- 3.6 锁操作
- 3.7 数组操作
- 3.8 注册和反注册native方法
- 3.9 异常Exception操作
- 3.10 引用的操作
- 3.11 其它
- 四、小结
一、概述
JNIEnv(Java Native Interface Environment) 是一个JNI接口指针 (每个线程独有一个 JNIEnv 指针),指向了本地方法的一个函数表,该函数表中的每一个成员指向了一个JNI函数,本地的方法通过JNI函数来访问JVM中的数据结构,详情如下图:

关联文章:
- NDK(一):NDK的集成
- NDK(二):JNI的数据结构
- NDK(三):JNIEnv解析
- NDK(四):Native与Java层互调
- NDK(五):JNI静态注册与动态注册
参考文章:
- JNI Functions 官方文档
二、JNIEnv结构体
我们知道 JNI 方法一般都是使用 JNIEnv 去调用,而 JNIEnv 又是一个指针,所以JNI中有哪些函数,只需要找到 JNIEnv 的实现体就可以了。
struct _JNIEnv;
# C中直接使用JNINativeInterface指针进行操作。
typedef const struct JNINativeInterface* C_JNIEnv; #if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
从上述代码可以看到,C中直接使用 JNINativeInterface 指针进行操作。在C++文件中是对_JNIEnv 起的一个别名 JNIEnv。
下面我们来看下 _JNIEnv 结构体的定义。
struct _JNIEnv {/* do not rename this; it does not seem to be entirely opaque */const struct JNINativeInterface* functions;#if defined(__cplusplus)jint GetVersion(){ return functions->GetVersion(this); }jclass DefineClass(const char *name, jobject loader, const jbyte* buf,jsize bufLen){ return functions->DefineClass(this, name, loader, buf, bufLen); }jclass FindClass(const char* name){ return functions->FindClass(this, name); }// ...略
}
通过上面的代码可知,_JNIEnv 内部所有的操作都是委托给JNINativeInterface指针进行操作的,相当于_JNIEnv只是一个代理层。而在C语言中直接使用的是JNINativeInterface指针,这就是JNIEnv在C和C++调用方式不一致的原因。
三、JNINativeInterface结构体
下面我们来分析一下 JNINativeInterface 的结构体,JNINativeInterface 结构体中主要包含如下几类的操作:
- Class操作
- 反射操作
- 对象字段 & 方法操作
- 类的静态字段 & 静态方法操作
- 字符串操作
- 锁操作
- 数组操作
- 注册和反注册native方法
- 异常Exception操作
- 引用的操作
下文中 JNINativeInterface 内的方法有时会省略一些参数信息,我们可以通过 JNI Functions 官方文档 来查看函数原型。
以 FindClass 函数为例:
struct JNINativeInterface {jclass (*FindClass)(JNIEnv*, const char*);
}// 函数原型为:
jclass FindClass(JNIEnv *env, const char *name);
详情如下图所示:

3.1 Class操作
struct JNINativeInterface {/*获取当前JNI版本信息:*/jint (*GetVersion)(JNIEnv *);// 定义一个类:类是从某个字节数组buf中读取出来的jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*, jsize);// 查找全限定名为name的类:如String类:"java/lang/String"jclass (*FindClass)(JNIEnv*, const char*);// 获取当前类的父类:通常在使用FindClass获取到类之后,再调用这个函数jclass (*GetSuperclass)(JNIEnv*, jclass);
}
3.2 反射操作
struct JNINativeInterface {// 将一个Method对象转换为jmethodIDjmethodID (*FromReflectedMethod)(JNIEnv*, jobject);jfieldID (*FromReflectedField)(JNIEnv*, jobject);// 通过jmethodID,反射得到Method对象jobject (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean);/* spec doesn't show jboolean parameter */jobject (*ToReflectedField)(JNIEnv*, jclass, jfieldID, jboolean);
}
3.3 对象字段 & 方法操作
struct JNINativeInterface {// 通过指定jclass类名、字段名称、字段类型来获取jfieldIDjfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);// ---------- 操作Field -------------// 通过类的jobject和jfieldID获取字段的jobject对象。void (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID);// 8种基本类型字段的获取与赋值。void (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte);jbyte (*GetByteField)(JNIEnv*, jobject, jfieldID);// ---------- 操作Method -------------// 通过指定jclass类名、方法名称、方法签名信息来获取jmethodIDjmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);// 8种基本类型:boolean、byte、char、short、int、long、float、double。// ...是可变长度的参数,参数类型相同。jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);// va_list是可变长度的参数,参数类型可以不同。jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);// jvalue 是8中基本类型jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
}typedef union jvalue {jboolean z;jbyte b;jchar c;jshort s;jint i;jlong j;jfloat f;jdouble d;jobject l;
} jvalue;
小结:
- 方法参数通过支持3种不同的参数类型来覆盖所有的参数场景。
- 单个参数场景:使用 jvalue 来表示支持一个参数的场景。
- 多个相同参数场景:使用 … 来表示支持同类型的多个参数。
- 多个不同参数场景:使用 va_list 来表示支持不同类型的多个参数。
3.4 类的静态字段 & 静态方法操作
struct JNINativeInterface {// ---------- 操作 Static Field ------------- jfieldID (*GetStaticFieldID)(JNIEnv*, jclass, const char*, const char*);void (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject);jobject (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);void (*SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte);jbyte (*GetStaticByteField)(JNIEnv*, jclass, jfieldID);// ---------- 操作 Static Method -------------// 与GetMethodID方法类似。jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);// 三种参数方式:...、va_list、jvalue。jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...);jbyte (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list);jbyte (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
}
3.5 字符串操作
struct JNINativeInterface {jstring (*NewString)(JNIEnv*, const jchar*, jsize);jsize (*GetStringLength)(JNIEnv*, jstring);const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);void (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);jstring (*NewStringUTF)(JNIEnv*, const char*);jsize (*GetStringUTFLength)(JNIEnv*, jstring);/* JNI spec says this returns const jbyte*, but that's inconsistent */const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);void (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);
}
3.6 锁操作
struct JNINativeInterface {jint (*MonitorEnter)(JNIEnv*, jobject);jint (*MonitorExit)(JNIEnv*, jobject);
}
3.7 数组操作
struct JNINativeInterface {jbyteArray (*NewByteArray)(JNIEnv*, jsize);jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);void (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray, jbyte*, jint);void (*SetByteArrayRegion)(JNIEnv*, jbyteArray, jsize, jsize, const jbyte*);void (*GetByteArrayRegion)(JNIEnv*, jbyteArray, jsize, jsize, jbyte*);
}
3.8 注册和反注册native方法
struct JNINativeInterface {jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*, jint);jint (*UnregisterNatives)(JNIEnv*, jclass);
}
动态注册JNI代码时会使用 RegisterNatives 函数。具体请参考 - NDK(五):JNI静态注册与动态注册
3.9 异常Exception操作
struct JNINativeInterface {jint (*Throw)(JNIEnv*, jthrowable);jint (*ThrowNew)(JNIEnv *, jclass, const char *);jthrowable (*ExceptionOccurred)(JNIEnv*);void (*ExceptionDescribe)(JNIEnv*);void (*ExceptionClear)(JNIEnv*);void (*FatalError)(JNIEnv*, const char*);
}
3.10 引用的操作
struct JNINativeInterface {// 全局变量的创建与删除jobject (*NewGlobalRef)(JNIEnv*, jobject);void (*DeleteGlobalRef)(JNIEnv*, jobject);// 局部变量的创建与删除jobject (*NewLocalRef)(JNIEnv*, jobject);void (*DeleteLocalRef)(JNIEnv*, jobject);// 对象的比较jboolean (*IsSameObject)(JNIEnv*, jobject, jobject);}
3.11 其它
struct JNINativeInterface {jint (*GetJavaVM)(JNIEnv*, JavaVM**);jobjectRefType (*GetObjectRefType)(JNIEnv*, jobject);jint (*PushLocalFrame)(JNIEnv*, jint);jobject (*PopLocalFrame)(JNIEnv*, jobject);
}
四、小结
- JNIEnv 是一个代理,实际的操作全部委托给 JNINativeInterface 指针执行。
- JNINativeInterface 结构体中主要包含如下几类的操作:
- Class操作
- 反射操作
- 对象字段 & 方法操作
- 类的静态字段 & 静态方法操作
- 字符串操作
- 锁操作
- 数组操作
- 注册和反注册native方法
- 异常Exception操作
- 引用的操作
相关文章:
NDK(三):JNIEnv解析
文章目录一、概述二、JNIEnv结构体三、JNINativeInterface结构体3.1 Class操作3.2 反射操作3.3 对象字段 & 方法操作3.4 类的静态字段 & 静态方法操作3.5 字符串操作3.6 锁操作3.7 数组操作3.8 注册和反注册native方法3.9 异常Exception操作3.10 引用的操作3.11 其它四…...
禅道——图文安装及使用教程
👨💻作者简介:练习时长两年半的java博主 📖个人主页:君临๑ 🎞️文章介绍:禅道的2023版安装图文教程 🎁 如果文章对你有用,就点个免费的赞吧👍 目录 一、搜…...
Java基础——枚举类enum
枚举类是一种特殊的数据类型,可以理解为一个数组,数组成员为特定的对象枚举类不能在外面创建对象,在类里面就包含了一组特定的对象,每个对象有着相同数量的属性枚举类的对象放在最前面,且对象们的顺序就是对应的索引枚…...
【机器学习】一文了解如何评估和选择最佳机器学习模型并绘制ROC曲线?
一文了解如何评估和选择最佳机器学习模型? 问ChatGPT:如何选择最佳机器学习模型?问ChatGPT:评估机器学习模型有哪些指标?0. 引言1. 混淆矩阵2. 评价指标3. ROC与AUC4. PR(precision recall )曲线参考资料问ChatGPT:如何选择最佳机器学习模型? 选择最佳机器学习模型是机…...
vue3 笔记
watchEffect 的起源 stackoverflow - watchEffect vs. watch watch behavior in v3 is different to v2Change watch Options API to trigger immediately vue3 最初只有 watch ,没有 watchEffect。这个时候的 watch 默认是 immediate true,可以 wat…...
第12章_MySQL数据类型精讲
第12章_MySQL数据类型精讲 🏠个人主页:shark-Gao 🧑个人简介:大家好,我是shark-Gao,一个想要与大家共同进步的男人😉😉 🎉目前状况:23届毕业生,…...
二叉树路径总和第一题
1题目 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。 叶子节点 是指没有…...
@RefreshScope源码解析
前言 RefeshScope这个注解想必大家都用过,在微服务配置中心的场景下经常出现,它可以用来刷新Bean中的属性配置,那么它是如何做到的呢?让我们来一步步揭开它神秘的面纱。 RefreshScope介绍 就是说我们在修改了bean属性的时候项目…...
【开发】后端框架——Spring
前置知识:JSP&Servlet 学习视频:https://www.bilibili.com/video/BV1WE411d7Dv?spm_id_from333.999.0.0 IoC:控制反转 IoC的理解:IoC思想,IoC怎么创建对象,IoC是Spring的核心 依赖注入三种方式&#x…...
vue中的自定义指令
前言 说到 vue 中的自定义指令,相信大家都不陌生。在官网中是这么说的,除了核心功能默认内置的指令 (v-model 和 v-show),vue 也允许注册自定义指令。那什么时候会用到自定义指令呢?代码复用和抽象的主要形式是组件。然而…...
技术分享及探讨
前言 很高兴给大家做一个技术分享及探讨。 下面给大家分享几个工作遇到有趣的例子。 docker docker 进程 现象 客户的模型导入到BML平台发布预测服务后,模型本身是用django提供的支持。按照本地docker的方式进行调试,kill掉django的进程修改代码…...
人工智能AI
AI 模型。它使用深度神经网络,从数十亿或数万亿个单词中学习,能够生成任何主题或领域的文本。它可以执行各种自然语言任务,如分类、总结、翻译、生成和对话。 大语言模型开发建立在4个核心思想上: 模型 – Models 提示词 - Prompt…...
2022天梯赛补题
题目详情 - L2-041 插松枝 (pintia.cn) 思路:模拟 背包就是个栈,开个stack解决流程思路是,每次取推进器前,尽可能拿背包的,背包拿到不可以时,跳出拿推进器时判断: 如果背包装得下,…...
字节跳动测试岗面试挂在2面,复盘后,我总结了失败原因,决定再战一次...
先说下我基本情况,本科不是计算机专业,现在是学通信,然后做图像处理,可能面试官看我不是科班出身没有问太多计算机相关的问题,因为第一次找工作,字节的游戏专场又是最早开始的,就投递了…...
Nodejs实现通用的加密和哈希算法(MD5、SHA1、Hmac、AES、Diffie-Hellman、RSA),crypto模块详解
crypto crypto模块的目的是为了提供通用的加密和哈希算法(hash)。用纯JavaScript代码实现这些功能不是不可能,但速度会非常慢。Nodejs用C/C++实现这些算法后,通过cypto这个模块暴露为JavaScript接口,这样用起来方便,运行速度也快。 MD5和SHA1 MD5是一种常用的哈希算法,…...
测试行业3年经验,从大厂裸辞后,面试阿里、字节全都一面挂,被面试官说我的水平还不如应届生
测试员可以先在大厂镀金,以后去中小厂毫无压力,基本不会被卡,事实果真如此吗?但是在我身上却是给了我很大一巴掌... 所谓大厂镀金只是不卡简历而已,如果面试答得稀烂,人家根本不会要你。况且要不是大厂出来…...
安卓悬浮窗口, 丝滑双指缩放视频窗口
最重要的事情说前面: demo源码:https://github.com/5800LDW/ProjectFloatingWindow前言:1.跨应用的浮动窗口在网上很多资料, 就不细说了。2.双指缩放View 也很多资料, 可参考:https://blog.csdn.net/zxq614/article/details/88873729正文下面进入正题, 如何把上述结合起来, 下面…...
300左右哪款蓝牙耳机适合学生用?四款便宜质量好的蓝牙耳机推荐
近年来,随着蓝牙耳机的发展,不管是音质、外观、佩戴还是降噪都有了很大的提升。但是我们在入手蓝牙耳机时,最好还是根据预算和需求入手。在此,我来给预算在三百内的朋友推荐几款便宜质量好的蓝牙耳机,可以当个参考。 …...
桥梁设计模式
介绍 Java桥梁模式(也称桥接模式)(Bridge Pattern)是一种设计模式,它将抽象和实现分离,使它们可以独立地变化.它通过一个大类或者一系列紧密关联的类拆分成两个独立的层次结构来实现这种分离,其中一个层次结构包含抽象类或接口,另一个层次结构包含实现类.桥梁模式使得抽象类和…...
【华为OD机试 2023最新 】 新员工座位(C++)
文章目录 题目描述输入描述输出描述用例题目解析C++题目描述 工位由序列F1,F2…Fn组成,Fi值为0、1或2。其中0代表空置,1代表有人,2代表障碍物。 1、某一空位的友好度为左右连续老员工数之和, 2、为方便新员工学习求助,优先安排友好度高的空位, 给出工位序列,求所有空…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...
Xela矩阵三轴触觉传感器的工作原理解析与应用场景
Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知,帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量,能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度,还为机器人、医疗设备和制造业的智…...
WEB3全栈开发——面试专业技能点P7前端与链上集成
一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染(SSR)与静态网站生成(SSG) 框架,由 Vercel 开发。它简化了构建生产级 React 应用的过程,并内置了很多特性: ✅ 文件系…...
