JNI
JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性,但是同时JNI会提升程序的性能。
通常情况下,下面几种情况下才会使用JNI技术:
-
需要调用Java语言不支持但依赖于操作系统平特性的一些功能。例如,需要调用当前Linux系统的某功能而Java却不支持。
-
整合以前以非Java语言开发的系统,毕竟重复利用这些Native语言库也符合代码复用的编程原则。例如,要用到早期实现的C/C++语言开发的一些功能或系统,将这些功能或系统整合到新的系统或版本中。
-
节省运行时间,提高运行效率(需借用C/C++)。例如,游戏、音视频开发涉及到图像、音频解码这种要求效率的功能。
从上图可知:
- Java虽具有和平台无关的特性,但Java和具体平台之间的隔离却是由JNI层来做到的。Java是通过JNI层调用Linux OS中的系统调用来完成对应的功能的。例如创建一个文件、创建一个Socket等。
- 除了Java世界外,还有一个核心的Native世界,它为整个系统高效和平稳的运行提供了强有力的支持。一般而言,Java世界经由JNI层通过IPC方式和Native世界交互。而Android平台上最为神秘的IPC方法就是Binder了。在第六章将详细介绍Binder。除此之外,Socket也是常用的IPC方式。这些内容在后面的代码分析中都会见到。
Native方法的注册
native_init方法是如何找对自己所对应的android_media_MediaRecorder_native_init方法呢?这就涉及到了本节所述JNI方法的注册问题了。
Native方法的注册分为静态注册(常用于应用侧的NDK开发)和动态注册(常用于系统中Framework开发)。
声明一个native函数。native为Java的关键字,表示它将由JNI层完成。
当在Java中调用init_native方法时,Java虚拟机就会去JNI中寻找Java_com_example_myapplication_MediaRecorder_native_1init函数,如果没找到会报错,如果找到了就会为这两者建立关联(通过保存JNI的函数指针,这样在下次调用native_init方法时直接使用这个函数指针即可)
native_init和processFile函数前都有Java的关键字native,它表示这两个函数将由JNI层来实现。
Java层的分析到此结束。JNI技术也很照顾Java程序员,只要完成下面两项工作就可以使用JNI了,它们是:
· 加载对应的JNI库。
· 声明由关键字native修饰的函数。
既然Java native函数数和JNI函数是一一对应的,那么是不是会有一个结构来保存这种关联关系呢?答案是肯定的。在JNI技术中,用来记录这种一一对应关系的,是一个叫JNINativeMethod的结构
/*需要注册的函数列表,放在JNINativeMethod 类型的数组中,
以后如果需要增加函数,只需在这里添加就行了
参数:
1.java中用native关键字声明的函数名
2.签名(传进来参数类型和返回值类型的说明)
3.C/C++中对应函数的函数名(地址)
*/
static JNINativeMethod gBYDDiagnosticClientMethods[] = {
{"nativeInit","()V", (void*)nativeInit },
{"nativeSendBuffer","([B)I", (void*)nativeSendBuffer },
{"nativeRelease", "()V", (void*)nativeRelease },
};
}; // namespace android