IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android NDK开发(五):JNI的数据类型说明及接口简介 -> 正文阅读

[移动开发]Android NDK开发(五):JNI的数据类型说明及接口简介

1 JNI简介

????????概念:JNI(Java Native Interface)java本地化接口,狭义上是SUN定义的一套标准接口,广义上是JNI中的接口、结构体、符号常量等等。

????????作用:实现?java 和 本地语言的数据类型的相互转换;实现java和本地语言互调彼此的方法

????????实现形式:JNI的具体实现是由本地语言(以C/C++为主)编写的数据类型、常量、函数、类、结构体等,是JDK中的一揽子本地文件,其中比较有代表性的jni.h及jni.cpp在OpenJDK的\openjdk\hotspot\src\share\vm\prims路径下,java程序启动时,JNI的代码被加载到java虚拟机中。

????????使用位置:在本地代码中使用,通过引用jni.h即可使用JNI定义的数据类型、方法、结构体等;利用JNI中定义的JNIEnv结构体,可通过函数指针调用JNI的函数。
(注意:JNI是java的,NDK是Android的)

????????效率:假设java调用java方法时间是1,java通过JNI调用本地方法时间约为3,本地通过JNI调用Java方法的时间大约是10。

2 JNI定义的数据类型、引用的种类及类型描述符

(1)JNI定义的与java数据类型对应的数据类型

????????JNI定义了java的数据类型与C/C++数据类型的对应关系,基本类型定义如下:

java基本数据类型JNI定义的在C/C++中对应的基本类型JNI起的别名(typedef)
shortshort(有符号16位)jshort
intint(有符号32位)jint
longlong long(有符号64位)jlong
floatfloat(有符号32位)jfloat
doubledouble(有符号64位)jdouble
charunsigned short(无符号16位)jchar
booleanunsigned char(无符号8位)jboolean
bytesigned char(有符号8位)jbyte

? ? ? ? 由上表可知,JNI用C/C++的部分基本类型来对应java的基本类型,那么java的引用类型对应C/C++的啥类型呢?请看下表:

java的引用类型JNI定义的在C++中对应的指针类型JNI起的别名(typedef)

Object类及其子类

class _jobject {};

_jobject*

jobject、jweak(JNI多定义的弱全局引用)
Class类

class _jclass : public _jobject {};

_jclass*

jclass
String类

class _jstring : public _jobject {};

_jstring*

jstring
class _jarray : public _jobject {};jarray(JNI数组类型的父类)
Object[ ]

class _jobjectArray : public _jarray {};

_jobjectArray*

jobjectArray
boolean[ ]

class _jbooleanArray : public _jarray {};

_jbooleanArray*

jbooleanArray
byte[ ]

class _jbyteArray : public _jarray {};

_jbyteArray*

jbyteArray
chart[ ]

class _jcharArray : public _jarray {};

_jcharArray*

jcharArray
short[ ]

class _jshortArray : public _jarray {};

_jshortArray*

jshortArray
int[ ]

class _jintArray : public _jarray {};

_jintArray*

jintArray
long[ ]

class _jlongArray : public _jarray {};

_jlongArray*

jlongArray
float[ ]

class _jfloatArray : public _jarray {};

_jfloatArray*

jfloatArray
double[ ]

class _jdoubleArray : public _jarray {};

_jdoubleArray*

jdoubleArray
Throwable[ ]

class _jthrowable : public _jobject {};

_jthrowable*

jthrowable

????????由上表可知,JNI用C++的指针类型来对应java的引用类型,JNI定义了C++的类型来对应Java中比较常用的类,并用C++相应类的指针类型,对应java常用类的引用类型;值得注意的是,JNI将java中的数组引用类型也对应成C++中的指针类型。

(2)JNI引用的种类

????????JNI定义的java的引用类型其本质是C++中的指针类型,这里我们称之为JNI中的引用类型,那么由JNI引用类型定义的变量的我们称之为JNI引用变量,JNI引用变量的值我们称之为JNI引用,JNI引用的种类如下:

JNI引用种类生命周期创建释放特点
局部引用(LocalRef)仅在当前线程当前方法中有效在本地方法中,若无特殊处理,创建的引用都是局部引用本地方法结束后,JavaVM自动释放;也可通过JNI方法DeleteLocalRef手动释放

所引用的对象不会被GC;

全局引用(GlobalRef)多线程、多方法中有效需要通过JNI方法NewGlobalRef并基于局部引用手动创建需要通过JNI方法DeleteGlobalRef手动释放所引用的对象不会被GC;
弱全局引用(GlobalWeakRef)多线程、多方法中有效需要通过JNI方法NewGlobalWeakRef并基于局部引用手动创建需要通过JNI方法DeleteGlobalWeakRef手动释放所引用的对象不保证不被GC;

(注意:对于LocalRef,每个JavaVM有一个LocalRefTable(局部引用表),最大可存储512个局部引用,如果局部引用超过512将报错,导致程序崩溃,如果本地方法在结束前会创建大量局部引用,一定要手动释放!!)
(注意:局部引用、全局引用和局部引用变量、全局引用变量得定义方式是不同的,局部引用变量、全局引用变量是看变量是定义在函数内还是函数外)
(注意:一般不使用GlobalWeakRef!)
(注意:不要试图将局部引用赋值给全局引用变量,即不要缓存局部引用,若需缓存,请将局部引用转为全局引用并赋值给全局引用变量;也不要将全局引用赋值给局部引用变量,这没有意义。)
(注意:一般不使用GlobalWeakRef!)

(3)JNI定义的其他数据类型

????????JNI除了定义了与java数据类型对应的数据类型之外,还定义了一些自己的数据类型,都定义在jni.h头文件中,具体如下:

1)jfieldID及jmethodID? ? ??

????????定义:
????????????????struct _jfieldID; ? ? ? ? ? ? ? ? ? ? ? /* opaque structure */
????????????????typedef struct _jfieldID* jfieldID; ? ? /* field IDs */
????????????????struct _jmethodID; ? ? ? ? ? ? ? ? ? ? ?/* opaque structure */
????????????????typedef struct _jmethodID* jmethodID; ? /* method IDs */

????????说明:_jfieldID、_jmethodID是结构体类型, jfieldID、jmethodID是指向相应结构体的指针变量,通常只会用到jfieldID、jmethodID,jni.h中隐藏了_jfieldID、_jmethodID的实现细节。

????????作用:jfieldID用于本地调用Java类或对象的属性时,jmethodID用于本地调用Java类或对象的方法时。

2)JNINativeInterface、_JNIEnv、JNIEnv

? ? ? ? 定义:
?????????????????????
#if defined(__cplusplus)
????????????????typedef _JNIEnv JNIEnv;
????????????????typedef _JavaVM JavaVM;
????????????????#else
????????????????typedef const struct JNINativeInterface* JNIEnv;
????????????????typedef const struct JNIInvokeInterface* JavaVM;
????????????????#endif

????????????????struct JNINativeInterface{
? ? ? ? ? ? ? ? ? ? jint ?(*GetVersion)(JNIEnv *);
? ? ? ? ? ? ? ? ? ? ........
????????????????}

????????????????struct _JNIEnv {
? ? ????????????????const struct JNINativeInterface* functions;
? ????????????????? jint GetVersion() { return functions->GetVersion(this);?
? ? ????????????????.......
????????????????}

????????说明:JNINativeInterface 结构体中定义了JNI标准的全部方法,_JNIEnv 结构体采用修饰模式,内部持有JNINativeInterface结构体实例的指针,包装了JNINativeInterface结构体的方法。在C++中JNIEnv是_JNIEnv 结构体类型的别名,在C中JNIEnv是 JNINativeInterface* 指针类型的别名,一般我们直接使用JNIEnv,后面我们统一用JNIEnv描述。

????????作用:JNI的大部分方法都需通过JNIEnv实例调用。
(注意:JNIEnv的实例是与线程相关的,不同线程的JNIEnv实例彼此独立,线程间不能共享JNIEnv实例,所以不要尝试将JNIEnv实例缓存为全局的)
(注意:在C++与C中使用JNIEnv指针变量调用JNI方法时,使用方式稍有差别,后面会详细说)

3)JavaVM、JNIInvokeInterface

? ? ? ? 定义:
?????????????????????
#if defined(__cplusplus)
????????????????typedef _JNIEnv JNIEnv;
????????????????typedef _JavaVM JavaVM;
????????????????#else
????????????????typedef const struct JNINativeInterface* JNIEnv;
????????????????typedef const struct JNIInvokeInterface* JavaVM;
????????????????#endif

????????????????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*);
????????????????};

????????????????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*/
????????????????};

????????说明:JNIInvokeInterface 结构体中定义了本地代码操作JavaVM及获取JNIEnv的方法,_JavaVM 结构体采用修饰模式,内部持有JNIInvokeInterface 结构体实例的指针,包装了JNIInvokeInterface 结构体的方法。在C++中JavaVM是_JavaVM 结构体类型的别名,在C中JavaVM是 JNIInvokeInterface* 指针类型的别名,一般我们直接使用JavaVM,后面我们统一用JavaVM描述。

????????作用:用于获取JavaVM实例,通过JavaVM实例可获取跟本地线程相关的JNIEnv实例,从而让在本地开启的线程在没有java native方法传递来的JNIEnv实例的情况下 能获取JNIEnv实例调用java方法。
(注意:JavaVM是java虚拟机在JNI层的代表,在一个java进程中只有一个JavaVM实例,因此该进程的所有线程都可以使用这个JavaVM实例,故可考虑将JavaVM缓存为全局的)。
(注意:JavaVM结构体的方法指针指向java invocation API,Invocation API用于把JVM嵌入到本地程序中

???????4JNINativeMethod

? ? ? ? 定义:
????????????????
typedef struct {
? ? ????????????????const char* name;
? ? ????????????????const char* signature;
? ? ????????????????void* ? ? ? fnPtr;
????????????????} JNINativeMethod;

????????说明:变量name是Java中函数的名字;变量signature是Java方法通过描述符表示的方法签名
;变量fnPtr是函数指针,指向本地函数,前面都要接 (void *),其name与fnPtr是对应的,一个是java层方法名,一个是native方法名。

????????作用:用于JNI动态注册(后面会说),每个JNINativeMethod实例都表示一个本地方法与java方法的映射关系。

(4)JNI定义的描述符

? ? ? ? JNI定义描述符是为了让本地代码通过JNI调用java时,准确找到Java的类或方法,JNI描述符如下:

java元素JNI描述符
intI
longJ
byteB
shortS
charC
floatF
doubleD
booleanZ
voidV
引用类型L+全类名;
类型[][类型
方法(形参类型)返回值类型

例子:

String[][Ljava/lang/String;
int[][][[I
long f (int n, String s, int[] arr);(ILjava/lang/String;[I)J

(注意:引用类型是L+全类名;,并将全类名中的“.” 换成“/”,在最后加上;)

(5)JNI定义的宏

1)JNIEXPORT

? ? ? ? 定义:
????????????????
#define JNIEXPORT ?__attribute__ ((visibility ("default")))

? ? ? ? 作用:用于表明这个函数是一个可导出函数。每一个C/C++库都有一个导出函数列表,只有在这个列表里面的函数才可以被外部直接调用,类似Java的public函数和private函数的区别。
(注意:也可在JNI函数前直接加__attribute__ ((visibility ("default"))),若将“default”改成“hidden”,则外界无法找到该函数)

2)JNICALL

? ? ? ? 定义:
???????????????????????
#define JNICALL

? ? ? ? 作用:只用于说明说明这个函数是一个JNI函数,用来和普通的C/C++函数进行区别,定义JNI函数时可以不加。

3)JNI_FALSE?及?JNI_TRUE

? ? ? ? 定义:
????????????????#define JNI_FALSE ? 0
? ? ? ? ? ? ? ? #define JNI_TRUE ? ?1

? ? ? ? 作用:JNI定义的字符常量,用来在本地代码中表示真假。

3 JNI定义的接口简介

? ? ? ?JNI定义的接口全在jni.h中,根据接口的实现方式不同可分为JNI自己实现的、间接调用java invocation API的、.间接调用so的,java invocation API及so为JNI暴露一些有用的方法。以下为方法简介,后续会结合使用详细说明:

接口位置实现作用
JNI定义的接口JNINativeInterface结构体中的接口JNI自己本地代码与java代码交互
JNIInvokeInterface结构体中的接口及JNI_CreateJavaVM、JNI_GetCreatedJavaVMs、JNI_GetDefaultJavaVMInitArgs调用java invocation API相关的方法用于将JVM嵌入到本地程序中
JNI_OnLoad
JNI_OnUnload
调用.so库相关方法java加载、卸载so库时,会在本地层回调,可用于JNI动态注册
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-13 23:35:31  更:2022-04-13 23:54:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 21:39:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码