翻译:https://developer.android.com/training/articles/perf-jni.html
参阅:《深入理解Android 卷I》
博文是对一些概念的总结,还需参阅着上述书籍来看。
JNI是Java Native Interface。它定义了一种托管代码(以Java编程语言编写)与本地代码交互(以C / C ++编写)的方式。它是供应商中立的,支持从动态共享库加载代码,虽然麻烦但是是有效的。
它是一座Native世界与Java世界链接的桥梁。
如果您还不熟悉它,请阅读Java Native Interface Specification以了解JNI的工作原理以及可用的功能。
JavaVM and JNIEnv
JNI定义了两个的关键数据结构“JavaVM”和“JNIEnv”。这两个都是指向函数表的指针。 JavaVM提供了“调用接口”功能,允许您创建和销毁JavaVM。理论上讲,每个进程可以有多个JavaVM,但Android只允许一个。
JNIEnv提供了许多JNI系统函数。Native函数第一个参数就是JNIEnv。
JNIEnv用于线程本地存储,是一个与线程相关的代表JNI环境的结构体。因此,您不能在线程之间共享JNIEnv。如果代码中无法来获取其JNIEnv,则应该共享JavaVM,并使用GetEnv来获取线程的JNIEnv。
Threads
所有线程都是Linux线程,被内核调度执行。它们通常从托管代码(使用Thread.start)启动,但也可以在其他地方创建,然后依附在JavaVM上。例如,使用pthread_create启动的线程可以通过JNI的 AttachCurrentThread或AttachCurrentThreadAsDaemon函数依附在JavaVm上。在线程被依附之前,它没有JNIEnv,不能进行JNI调用。
Android不会挂起正在执行Native代码的线程。如果正在进行垃圾收集,或调试器发出挂起请求,Android将在下一次进行JNI调用时暂停线程。
通过JNI附加的线程必须在退出之前调用DetachCurrentThread函数。
jclass, jmethodID, and jfieldID
如果要从Native代码访问对象的字段,则应该执行以下操作:
- 使用FindClass方法获取该类的类对象引用
- 使用GetFieldID方法获取该字段的field ID
- 使用恰当的方法获取该字段的内容,例如GetIntField
注意:
如果每次操作jobject前都去查询jmethodID和jfieldID,那么将会影响程序运行的效率,所以 我们可以在初始化后可以取出这些id并保存以供后续使用。
注册JNI函数
- 静态注册
- 编写java代码,编译生成class文件
- javah生成头文件
-
动态注册
- 实现JNI_OnLoad函数
-
维护JNINativeMethod结构体,存储Java中的Native函数名、此函数的签名信息(由参数和返回值组成)、JNI层对应的函数指针(Void * 类型)
3.注册方法执行如下操作