Android进阶解密读书笔记(九)——JNI原理

JNI是Java Native Interface的缩写,一般有以下情况会用到JNI:

  • 需要调用java语言不支持的依赖于操作系统平台特性的一些功能
  • 为了整合一些以前的非java语言开发的系统
  • 为了节省程序的运行时间,必须采用其他语言来提升运行的效率

1、Native方法注册

(1)静态注册

  1. 新建 xx.java 也就是 JNI 对应的 Java层的类其中定义好要使用的方法
  2. 在xx.java目录下执行下面两条命令
    javac com/example/xx.java
    javah com.example.xx
    第二条命令会在当前目录下生成 com_example_xx.h 文件,其中会生成对应的C++ 层方法,命名格式为 包名+类名+方法名的格式,用"_"分割
    静态注册就是根据方法名,将Java方法和JNI函数建立关联,但它有以下缺点:
    1、JNI层的函数名称过长;
    2、声明Native方法类需要用javah生成头文件
    3、初次调用Native方法时需要建立关联,影像效率

   (2) 动态注册

        JNI中有一种结构用来记录 Java 的 Native 方法和JNI 方法的关联关系,它就是 JNINativeMethod,它在 jni.h 中被定义

typedef struct{
    const char* name;//Java方法的名字
    const char* signature;//Java方法的签名信息
    void* fnPtr; //JNI中对应的方法指针
}JNINativeMethod;

系统的 MediaRecorder采用的就是动态注册,我们来看它的JNI层是怎么做的

  1. 定义 JNINativeMethod类型的数组 gMethods,里面存储的就是 MediaRecorder 的 Native 方法与 JNI 层函数的对应关系
    //frameworks/base/media/jni/android_media_MediaRecorder.cpp
    static const JNINativeMethod gMethods[] = {
        {"start", "()V", (Void*)android_media_MediaRecord_Start}, ...
    }

    其中start 是 java 层的Native方法,对应的JNI 层的函数为 android_media_MediaRecord_start; ()V是start方法的签名信息。
    定义完该数组后还需注册它,注册的函数为 register_android_media_MediaRecorder。JNI_OnLoad 函数会在调用 System.LoadLibrary 函数后调用,因此,注册函数就被统一定义在android_media_MediaPlayer.cpp 的 JNI_OnLoad函数中

  2. 在register_android_media_MediaRecorder 方法中返回了 AndroidRuntime 的 registerNativeMethods 函数,也就是会调用该方法

  3. 在Android Runtime的registerNativeMethods函数中又返回了jniRegisterNativeMethods函数,它被定义在 JNI 帮助类 JNIHelp.cpp 中

  4. jniRegisterNativeMethods 方法中最终通过调用的 JNIEnv 的 RegisterNatives 函数来完成JNI的注册

2、数据类型的转换

  1. 基本数据类型的转换
    基本数据类型转换除了最后一行的 void,其他的数据类型只需要在前面加上 “j” 就可以了,第三列的 Signature 代表签名格式
    JavaNativeSignature
    bytejbyteB
    charjcharC
    booleanjbooleanZ
    voidvoidV
  2. 引用数据类型的转换
    数组的 JNI 层数据类型需要以 “Array” 结尾,签名格式的开头都会有 “I”;需要注意有些数据类型的签名以 ";” 结尾,引用数据类型还具有继承关系
    jclass、jstring、jarray和jthrowable都继承 jobject,而 jobjectArray、jintArray和jlongArray等类型都继承 jarray

3、方法签名

方法签名就是由签名格式组成的。

方法签名的作用:Java是具有重载方法的;可以定义方法名相同,但参数不同的方法,正因为如此,在JNI中仅仅通过方法名是无法找到 Java 中对应的具体方法的,JNI 为了解决这一问题就将参数类型和返回值类型组合在一起作为方法签名。通过方法签名和方法名就可以找到对应的Java方法

JNI的方法签名的格式为:(参数签名格式......)返回值签名格式

java提供了javap命令来自动生成签名,对使用 javac 命令生成的 class 文件使用:

//s 表示输出内部类型签名
//p 表示打印出所有的方法和成员,默认打印public成员
javap -s -p xx.class

4、解析JNIEnv

JNIEnv是Native世界中java环境的代表。通过JNIEnv*指针可以在Native世界中访问Java世界的代码进行操作,它只在创建它的线程中有效,不能跨线程传递,因此不同线程的JNIEnv是彼此独立的,JNIEnv的主要作用有以下两点:

  • 调用Java方法
  • 操作Java中的变量和对象等

JNIEnv的类型都和JNINativeInterface结构有关,在JNINativeInterface结构中定义了很多和JNIEnv结构体对应的函数指针,通过这些函数指针的定义,就能够定位到虚拟机中的JNI函数表,从而实现了JNI层在虚拟机中的函数调用,这样JNI层就可以调用Java世界中的方法了

5、引用类型

JNI的引用类型分为三种分别是本地引用(Local Reference)、全局引用(Global Reference)和弱全局引用(Weak Global Reference)

  1. 本地引用
    JNIEnv提供的函数所返回的引用基本上都是本地引用,因此本地引用也是JNI中最常见的引用类型。本地引用的特点主要有以下几点:
    (1)当Native函数返回时,这个本地引用就会被自动释放
    (2)只有在创建它的线程中有效,不能够跨线程使用
    (3)局部引用是JVM负责的引用类型,受JVM管理
  2. 全局引用
    全局引用和本地引用几乎是相反的,它主要有以下特点:
    (1)在Natvie函数返回时不会被自动释放,因此全局引用需要手动来进行释放,并且不会被GC回收
    (2)全局引用是可以跨线程使用的
    (3)全局引用不受JVM管理
  3. 弱全局引用
    弱全局引用是一种特殊的全局引用,它和全局引用的特点相似,不同的是弱全局是可以被GC回收的,被GC回收后会指向NULL
    JNEEnv的NewWeakGlobalRef函数用来创建弱全局引用,调用JNIEnv的DeleteWeakGlobalRef函数用来释放弱全局引用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值