JNI 简介
JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植;从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互
本地代码与 Java 虚拟机之间是通过 JNI 函数实现相互操作的;JNI 函数通过接口指针来获得,本地方法将 JNI 接口指针当作参数来接受;虚拟机保证在从相同的 Java 线程中对本地方法进行多次调用时,传递给本地方法的接口指针是相同的,本地方法被不同的 Java 线程调用时,它接受不同的 JNI接口指针
使用JNI和算法进行交互,主要是为了提高算法的性能,最大化的利用机器硬件资源
Android 中的语言层
可以将语言层分成Java层和Native层;为什么会有Native层呢?是因为在Java出现之前,很多的功能和系统都是由Native语言写的,所以Java出现之后就不必重复造轮子,直接调用这些方法就行了,而且Native层更接近汇编语言,所以性能更优
所以代码中就出现了Java层调用Native层的方法,而这个跨语言的调用过程就是 JNI
下图就是他们的关系:
当Java语言无法处理一些任务的时候,就可以使用JNI来完成
下面是几个 JNI 的应用场景:
需要调用Java语言不支持的依赖于操作系统平台特性的一些功能
● 需要调用当前UNIX系统的某个功能,而Java不支持这个功能的时候,就要用到JNI
● 在程序对时间敏感或对性能要求特别高时,有必要用到更底层的语言来提高运行效率
● 音视频开发涉及到的音视频编解码需要更快的处理速度,这就需要用到JNI
● 为了整合一些以前的非Java语言开发的系统
● 需要用到早期实现的C/C++语言开发的一些功能或者系统,将这些功能整合到当前的系统或者新的版本中
JNI是完善Java的一个重要功能,它让Java更加全面、封装了各个平台的差异性
JNI在 Android 开发里的应用主要有:
● 音视频开发
● 热修复
● 插件化
● 逆向开发
● 等等…
为了更好的使用JNI,Android提供了NDK这个工具,NDK基于JNI,所以理解了JNI,NDK也会很容易掌握
JNI 中的引用类型
Java中提供了四种引用类型,可以帮助垃圾回收。同样jni也提供了引用类型
● 本地引用(LocalReferences)
● 全局引用(Global References)
● 弱全局引用(Weak GlobalReferences)
本地引用
● JNIEnv 提供的函数中定义的非全局的引用基本上都是本地引用,因此本地引用也是JNI中最常见的引用类型
● 本地引用最大的特点当Native函数返回时,这个本地引用就会被自动释放;所以此引用是JVM负责的引用类型,受JVM管理
我们也可以使用JNIEnv的DeleteLocalRef函数来手动删除本地引用
全局引用
● 在native函数返回时不会被自动释放,因此全局引用需要手动来进行释放,并且不会被GC回收
● JNIEnv的NewGlobalRef函数用来创建全局引用,调用JNIEnv的DeleteGlobalRef函数来释放全局引用
全局引用不受到JVM管理
弱全局引用
● 是一种特殊的全局引用;它和全局引用的特点相似,不同的是弱全局是可以被GC回收的,弱全局引用被GC回收之后会指向NULL
● 在运行过程中可能会被垃圾回收;所以使用它之前,需要调用 JNIEnv 的 isSameObject 判断它是否被回收
JNI 的异常处理
● JNI 中也有异常,不过它和 C++、Java 的异常不太一样;如果调用 JNIEnv 的某些函数出错了,则会产生一个异常,但这个异常不会中断本地函数的执行,直到从 JNI 层返回到 Java 层后,虚拟机才会抛出这个异常
● 虽然在 JNI 层中产生的异常不会中断本地函数的运行;但一旦产生异常后,就只能做一些资源清理工作了(例如释放全局引用,或者 ReleaseStringChars);如果这时调用除上面所说函数之外的其他 JNIEnv 函数,则会导致程序死掉
JNI 层函数可以在代码中截获和修改这些异常,JNIEnv 提供了三个函数给予帮助:
● ExceptionOccured 函数,用来判断是否发生异常
● ExceptionClear 函数,用来清理当前 JNI 层中发生的异常
● ThrowNew 函数,用来向 Java 层抛出异常
JNI 特点:
二进制兼容
● 本地方法库与同一平台上所有Java 虚拟机之间实现二进制兼容,即对于给定平台开发人员只需要维护一种版本的本地方法库
效率高
为了实现实时系统,JNI 在效率与虚拟机无关性之间进行了优化,以保障高效运行
功能强
● JNI 提供了大量的函数及接口让本地方法与Java 虚拟机内核相互操作,增强两者的功能
本地代码与 Java 虚拟机之间是通过 JNI 函数实现相互操作的;JNI 函数通过接口指针来获得,本地方法将 JNI 接口指针当作参数来接受;虚拟机保证在从相同的 Java 线程中对本地方法进行多次调用时,传递给本地方法的接口指针是相同的,本地方法被不同的 Java 线程调用时,它接受不同的 JNI接口指针
尾述
点击 此处 即可 免费获取 更多学习笔记 、 面试视频
技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面
Android 架构师之路还很漫长,与君共勉
PS:有问题欢迎指正,可以在评论区留下你的建议和感受;
欢迎大家点赞评论,觉得内容可以的话,可以转发分享一下
网友评论