JNI 技术,是用来完成 Java 与其他语言的通信,就是说很多无法用 Java 实现,用其他语言实现效率更高或者其他语言已经有成熟的功能模块的时候就需要用 JNI。JNI 并不是 Android 特有的,而是在 Java 程序中就已经存在的一种技术,Android 对其进行了适配罢了。为了方便做 JNI 技术实现,Android 提供了 NDK 开发工具。这点就类似于 Binder 通信机制与 AIDL。
JNI 技术在 Android 中的实现分为三层,
-
Java 层 这层还是 java 实现的代码,只不过和普通 java 文件不同。里面会加载动态库及用 native 关键字修饰的方法代码。
-
JNI 层 这层的实现一般就是其他语言了,例如 C++ 的话就会对应一个 cpp 文件,当然方法名称一般不会和 Java 层定义的 native 方法名一样,所以这里有一个关键操作用来做两层之间方法的映射——JNI 方法注册
-
Native 层 真正的功能实现
JNI 方法注册
方法注册分为静态和动态,其中静态多用于 NDK 开发,动态多用于 Framework 开发,
-
静态注册 首先定义好 Java 层的内容,再通过命令行进行 javac 及 javah 编译,得到相对于的 .h 文件。.h 文件内描述的就是 java 层定义的 native 方法对应的 JNI 层方法。 将 .h 文件拷贝至 jni 文件夹内,再根据 .h 文件编写对应的 .cpp 文件。 最后需要用到 Android.mk 集成编译。 静态注册的本质就是通过方法指针与 JNI 进行关联。 上述这个过程我想应该是可以通过 NDK 做简化的,使得开发者只关心业务功能,不过具体 NDK 如何使用,后续还要去看看才行。
-
动态注册 动态注册恰好跟静态注册相反,不用提前编译出 .h 文件。在 Java 层加载动态库时,会调用 JNI_Onload 函数,在这个函数内,会根据具体情况最终调用 RegisterNatives 再进行 JNI 方法注册,也就是关联 Java 层的 Native 方法和 JNI 层的方法。 这里会涉及到一个数据结构,是用来描述 Java 层 Native 方法与 JNI 层方法的映射的——JNINativeMethod,通过对具体方法的关联映射,才能完成动态注册。
相关概念
与 JNI 技术实现相关的概念主要包括一些特殊数据结构或者数据类型,
-
JNIEnv 相当于 JNI 层或 Native 层对 Java 层的引用,通过它可以调用 Java 层的东西,不能跨线程转递,只能在获取它的线程中操作。JNIEnv 的作用就是 1.调用 Java 方法,2.操作 Java 对象或变量,所以虽然 JNIEnv 再解析下去会涉及到 JNINativeInterface 这个数据结构,但其本质是不变的。 jfieldID 代表 Java 类中的成员变量,jmethodID 代表 Java 类中的方法,jclass 代表 Java 类的 Class 对象。
-
JavaVM 相当于是在 JNI 层里的 Java 虚拟机,通过 JavaVM 的 AttachCurrentThread 函数可以获取到 JNIEnv,用完之后需要进行 DetachCurrentThread。
-
数据类型 与 Java 的数据类型对应(基本类型和引用类型),Native 层也有一系列的数据类型定义,例如 byte 对应于 jbyte,int 对应 jint,String 对应 jstring,char[] 对应 jcharArray,其他对象类型对应 jobject 等。
-
方法签名 其作用是为了区分重载方法,确保 Java 层 native 方法能一一对应 JNI 层方法。
-
引用类型 JNI 的引用类型分为本地引用,全局引用,弱全局引用。本地引用是最常用的,会随着 Native 函数返回而自动释放资源,不能跨线程使用,我的理解是这有点像方法里的局部变量;全局引用与本地引用相反,可以跨线程,不随 Native 函数返回而销毁,需要手动释放;弱全局就有点像定义为弱引用的成员变量,必要时会被回收释放。
参考内容
网友评论