JNI概念

JNI 全称 Java Native Interface ,Java本地化接口,可以通过 JNI 调用系统提供的API。

操作系统,无论是Linux,Windows还是Mac OS,或者一些汇编语言写的底层硬件驱动都是C/Cpp写的。

Java和C/Cpp不同,它不会直接编译成平台机器码,而是编译成虚拟机可以运行的Java字节码的.class文件,通过JIT技术即时编译成本地机器码,所以有些效率就比不上C/Cpp代码,JNI技术就解决了这一痛点

  • JIN技术可以说是C语言和Java语言交流的适配器、中间件

JNI 与 NDK 区别

  • JNI:JNI是一套编程接口,用来实现java代码与本地的C/Cpp代码进行交互
  • NDK: NDK 是Google开发的一套开发和编译工具集,可以生成动态链接库,主要用于Android的JNI开发

为什么要用NDK开发

很多时候利用NDK开发都是为了对数据进行加密操作,因为单纯的 Java 太容易被反编译了,加密算法也就很容易被破解,而利用C/Cpp开发可以加大破解难度。

Java语言执行流程

  • 编译字节码:Java编译器编译.Java源文件,获得class字节码文件
  • 装载类库:使用类装载器装载平台上的Java类库,并进行字节码验证
  • Java虚拟机:将字节码加入到 JVM 中,Java解释器和即时编译器同时处理字节码文件,将处理后的结果放入运行时系统
  • 调用 JVM 所在平台类库:JVM处理字节码后,转换成相应平台的操作,调用本平台地层类库进行相关处理

Java一次编译到处执行:JVM在不同的操作系统都有实现,Java可以一次编译到处执行,字节码文件一旦编译好了,可以放在任何平台的虚拟机上运行

jin.h

  • jni.h头文件就是为了让C/Cpp类型和Java原始类型相匹配的头文件定义

JNIEnv分析

  • JNIEnv是jni.h文件最重要的部分,它的本质是指向函数表指针的指针(JavaVM也是) ,函数表里面定义了很多JNI函数,同时它是区分C和Cpp环境的。

JNIEnv特点

  • JNIEnv是一个指针,指向一组 JNI 函数,通过这些函数可以实现 Java 层和 JNI 层的交互,就是说通过JNIEnv调用JNI函数可以访问Java虚拟机,操作Java对象
  • 所有本地函数都会接收 JNIEnv 作为第一个参数

不过Cpp的 JNI 函数已经对 JNIEnv参数进行了封装,不用写在函数参数上

  • 用作线程局部存储,不能在线程间共享一个 JNEnv 变量,也就是说 JNIEnv 只在创建它的线程有效,不能跨线程传递;相同的 Java 线程调用本地方法,所使用的 JNIEnv 是相同的,一个 native 方法不能被不同的Java线程调用

JNI 参数传递

  • 第一个参数为*JNIEnv * env,这个env可以看做是Jni接口本身的一个对象,jni.h头文件中存在着大量被封装好的函数,这些函数也是Jni编程中经常被使用到的,要想调用这些函数就需要使用JNIEnv这个对象*
  • 第二个参数为jobject obj
    • 这个jobject需要两种情况分析。
    • 如果调用的方法是一个非静态方法,在Java中要想调用它必须先实例化对象,然后再用对象调用它,那这个时候jobject就可以看作Java类的一个实例化对象,也就是obj就是t。
    • 如果调用的方法是一个静态方法,那么再Java中,它不是属于一个对象的,而是属于一个类的,这个时候jobject就可以看做是java类的本身。

NDK开发 - JNI 数组数据处理

JNI 中的数组分为基本类型数组和对象数组,它们的处理方式是不一样的

  • 基本类型数组中的所有元素都是 JNI 的基本数据类型,可以直接访问。

    • 获取数组元素都可以用 GetArrayElements 获取:利用 GetByteArrayElements 函数获取数组指针

    这个函数的第一个参数就是 JNIEnv * env,第二个参数就是java层传进来的数组,它返回一个指向byte数组类型的指针

  • 而对象数组中的所有元素是一个类的实例或其它数组的引用,和对象数组中的所有元素是一个类的实例或其它数组的引用,不能直接访问 Java 传递给 JNI 层的数组,必须选择合适的 JNI 函数来访问和设置 Java 层的数组对象