当前位置: 首页 > news >正文

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届毕业生&#xff0c…...

二叉树路径总和第一题

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 也允许注册自定义指令。那什么时候会用到自定义指令呢?代码复用和抽象的主要形式是组件。然而&#xf…...

技术分享及探讨

前言 很高兴给大家做一个技术分享及探讨。 下面给大家分享几个工作遇到有趣的例子。 docker docker 进程 现象 ​ 客户的模型导入到BML平台发布预测服务后,模型本身是用django提供的支持。按照本地docker的方式进行调试,kill掉django的进程修改代码…...

人工智能AI

AI 模型。它使用深度神经网络,从数十亿或数万亿个单词中学习,能够生成任何主题或领域的文本。它可以执行各种自然语言任务,如分类、总结、翻译、生成和对话。 大语言模型开发建立在4个核心思想上: 模型 – Models 提示词 - Prompt…...

2022天梯赛补题

题目详情 - L2-041 插松枝 (pintia.cn) 思路:模拟 背包就是个栈,开个stack解决流程思路是,每次取推进器前,尽可能拿背包的,背包拿到不可以时,跳出拿推进器时判断: 如果背包装得下,…...

字节跳动测试岗面试挂在2面,复盘后,我总结了失败原因,决定再战一次...

先说下我基本情况,本科不是计算机专业,现在是学通信,然后做图像处理,可能面试官看我不是科班出身没有问太多计算机相关的问题,因为第一次找工作,字节的游戏专场又是最早开始的,就投递了&#xf…...

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、为方便新员工学习求助,优先安排友好度高的空位, 给出工位序列,求所有空…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践

作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...

Xela矩阵三轴触觉传感器的工作原理解析与应用场景

Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知,帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量,能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度,还为机器人、医疗设备和制造业的智…...

沙箱虚拟化技术虚拟机容器之间的关系详解

问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西,但是如果把三者放在一起,它们之间到底什么关系?又有什么联系呢?我不是很明白!!! 就比如说: 沙箱&#…...

VisualXML全新升级 | 新增数据库编辑功能

VisualXML是一个功能强大的网络总线设计工具,专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑(如DBC、LDF、ARXML、HEX等),并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...

使用SSE解决获取状态不一致问题

使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...