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) | short | short(有符号16位) | jshort | int | int(有符号32位) | jint | long | long long(有符号64位) | jlong | float | float(有符号32位) | jfloat | double | double(有符号64位) | jdouble | char | unsigned short(无符号16位) | jchar | boolean | unsigned char(无符号8位) | jboolean | byte | signed 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嵌入到本地程序中)
???????4)JNINativeMethod
? ? ? ? 定义: ????????????????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描述符 | int | I | long | J | byte | B | short | S | char | C | float | F | double | D | boolean | Z | void | V | 引用类型 | 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动态注册 |
|