当前位置: 首页 > 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对象转换为jmethodID
    jmethodID   (*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类名、字段名称、字段类型来获取jfieldID
    jfieldID    (*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类名、方法名称、方法签名信息来获取jmethodID
	jmethodID   (*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操作
    • 引用的操作

相关文章:

  • 清理zabbix数据库ibdata1文件
  • 蛇形走线用在哪里,一文告诉你
  • 什么是“关键对话”?“关键对话”背后的底层思维是什么?如何进行一场“关键对话”?
  • java基础知识——11.方法
  • 什么是web3?未来趋势?怎么学?
  • 2023第二届浙江省技能大赛温州市选拔赛任务书
  • 技术分享及探讨
  • NDK(四):Native与Java互调
  • SpringSecurity
  • 机器学习:基于逻辑回归对优惠券使用情况预测分析
  • 米哈游春招后端-2023.03.19-第一题-米哈游的RBG矩阵-简单
  • 教你精通JavaSE语法之第九章、抽象类和接口
  • 龙芯2K1000开发板拷贝镜像到固态
  • 前端计算文件 hash
  • ChatGPT写作文章-快速使用ChatGPT不用注册方式
  • php的引用
  • SegmentFault for Android 3.0 发布
  • 【JavaScript】通过闭包创建具有私有属性的实例对象
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • 345-反转字符串中的元音字母
  • Android Studio:GIT提交项目到远程仓库
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • Android 架构优化~MVP 架构改造
  • angular学习第一篇-----环境搭建
  • CSS 三角实现
  • ERLANG 网工修炼笔记 ---- UDP
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • Java的Interrupt与线程中断
  • Java-详解HashMap
  • laravel5.5 视图共享数据
  • REST架构的思考
  • Unix命令
  • vue 个人积累(使用工具,组件)
  • 纯 javascript 半自动式下滑一定高度,导航栏固定
  • 讲清楚之javascript作用域
  • 使用 Docker 部署 Spring Boot项目
  • 移动端 h5开发相关内容总结(三)
  • 用Visual Studio开发以太坊智能合约
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • 正则学习笔记
  • 06-01 点餐小程序前台界面搭建
  • TPG领衔财团投资轻奢珠宝品牌APM Monaco
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • 进程与线程(三)——进程/线程间通信
  • 如何在 Intellij IDEA 更高效地将应用部署到容器服务 Kubernetes ...
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • #QT(智能家居界面-界面切换)
  • (2015)JS ES6 必知的十个 特性
  • (vue)页面文件上传获取:action地址
  • (阿里云万网)-域名注册购买实名流程
  • (待修改)PyG安装步骤
  • (十八)devops持续集成开发——使用docker安装部署jenkins流水线服务
  • (原创)Stanford Machine Learning (by Andrew NG) --- (week 9) Anomaly DetectionRecommender Systems...
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'