《深入理解Android 卷I》- JNI

时间:2022-11-19 00:59:12

翻译:https://developer.android.com/training/articles/perf-jni.html
参阅:《深入理解Android 卷I》

博文是对一些概念的总结,还需参阅着上述书籍来看。

JNI是Java Native Interface。它定义了一种托管代码(以Java编程语言编写)与本地代码交互(以C / C ++编写)的方式。它是供应商中立的,支持从动态共享库加载代码,虽然麻烦但是是有效的。

它是一座Native世界与Java世界链接的桥梁。
《深入理解Android 卷I》- JNI

如果您还不熟悉它,请阅读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。
《深入理解Android 卷I》- JNI

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函数

  • 静态注册
    1. 编写java代码,编译生成class文件
    2. javah生成头文件
  • 动态注册

    1. 实现JNI_OnLoad函数
    2. 维护JNINativeMethod结构体,存储Java中的Native函数名、此函数的签名信息(由参数和返回值组成)、JNI层对应的函数指针(Void * 类型)
      《深入理解Android 卷I》- JNI

      3.注册方法执行如下操作
      《深入理解Android 卷I》- JNI