JNI详解
一切诸法性皆如是,唯是自心分别境界。凡夫迷惑不能解了,无有能见亦无所见,无有能说亦无所说,见佛闻法皆是分别,如向所说不能见佛,不起分别是则能见。 ----------佛说
JNI编程
JNI是一种本地编程接口。它允许运行在JAVA虚拟机中的JAVA代码和用其他编程语言,诸如C语言、C++、汇编,写的应用和库之间的交互操作。
JNI数据类型
JNIEXPORT 和 JNICALL,定义在`jni_md.h`头文件中。
JNIEXPORT:
在 Windows 中,定义为`__declspec(dllexport)`。因为Windows编译 dll 动态库规定,如果动态库中的函数要被外部调用,需要在函数声明中添加此标识,表示将该函数导出在外部可以调用。
在 Linux/Unix/Mac os/Android 这种 Like Unix系统中,定义为`__attribute__ ((visibility ("default")))`GCC 有个visibility属性, 该属性是说, 启用这个属性:
1. 当-fvisibility=hidden时动态库中的函数默认是被隐藏的即 hidden. 除非显示声明为`__attribute__((visibility("default")))`.
2 当-fvisibility=default时动态库中的函数默认是可见的.除非显示声明为`__attribute__((visibility("hidden")))`.
JNICALL:
在类Unix中无定义,在Windows中定义为:`_stdcall ` ,一种函数调用约定 。类Unix系统中这两个宏可以省略不加
图1C/C++中获取java的数组
图二C/C++反射Java
1在C/C++中反射创建Java的对象,调用Java的方法
图三 图四基本数据类型的签名采用一系列大写字母来表示, 如下表所示:
图5可以使用javap来获取反射方法时的签名
cd 进入 class所在的目录 执行: javap -s 全限定名,查看输出的 descriptor
图6
反射属性
图7
图10
JNI引用
在 JNI 规范中定义了三种引用:局部引用(Local Reference)、全局引用(Global Reference)、弱全局引用(Weak Global Reference)。
局部引用
大多数JNI函数会创建局部引用。NewObject/FindClass/NewStringUTF 等等都是局部引用。
局部引用只有在创建它的本地方法返回前有效,本地方法返回后,局部引用会被自动释放。
因此无法跨线程、跨方法使用。
释放一个局部引用有两种方式:
1、本地方法执行完毕后VM自动释放; 2、通过DeleteLocalRef手动释放;
VM会自动释放局部引用,为什么还需要手动释放呢?因为局部引用会阻止它所引用的对象被GC回收。
全局引用
全局引用可以跨方法、跨线程使用,直到它被手动释放才会失效 。
由 NewGlobalRef 函数创建
图12弱引用
与全局引用类似,弱引用可以跨方法、线程使用。与全局引用不同的是,弱引用不会阻止GC回收它所指向的VM内部的对象 。
在对Class进行弱引用是非常合适(FindClass),因为Class一般直到程序进程结束才会卸载。
在使用弱引用时,必须先检查缓存过的弱引用是指向活动的对象,还是指向一个已经被GC的对象
图13JNI_OnLoad函数
调用System.loadLibrary()函数时, 内部就会去查找so中的 JNI_OnLoad 函数,如果存在此函数则调用。
JNI_OnLoad会:
告诉 VM 此 native 组件使用的 JNI 版本。
对应了Java版本,android中只支持JNI_VERSION_1_2 、JNI_VERSION_1_4、JNI_VERSION_1_6
在JDK1.8有 JNI_VERSION_1_8。
图14
动态注册
1 在此之前我们一直在jni中使用的 Java_PACKAGENAME_CLASSNAME_METHODNAME 来进行与java方法的匹配,这种方式我们称之为静态注册。
2 而动态注册则意味着方法名可以不用这么长了,在android aosp源码中就大量的使用了动态注册的形式
图15 图16
native跨线程调用Java
native调用java需要使用JNIEnv这个结构体,而JNIEnv是由Jvm传入与线程相关的变量。
但是可以通过JavaVM的AttachCurrentThread方法来获取到当前线程中的JNIEnv指针。
图16 图16 图16