4.1 JNI的本质
在Android系统中,JNI是连接Java部分和C/C++部分的纽带。要想完整地使用JNI,需要仔细分析Java代码和C/C++代码。在Android中通过提供JNI的方式,让Java程序可以调用C语言程序。Android中的很多Java类都具有Native(本地)接口,这些接口由本地实现,然后注册到系统中。
JNI调用的层次非常清晰,主要分为3层,在Android系统中这3层从上到下依次为:Java→JNI→C/C++(SO库), Java可以访问C/C++中的方法,同样C/C++可以修改Java对象,图4-1清晰地描述了这三者之间的调用关系。
图4-1 JNI调用的层次关系
由图4-1可知,JNI的调用关系为:
Java------------------------JNI------------------------Native
在Android 4.4的源码中,主要的JNI代码放在以下的路径中:
frameworks/base/core/jni/
上述路径中的内容被编译成库libandroid_runtime.so,这是一个普通的动态库,被放置在目标系统的“/system/lib”目录下。另外,Android中还存在其他的JNI库,其实JNI中的各个文件就是普通的C++源文件;在Android中实现的JNI库,需要连接动态库libnativehelper.so。
要想弄明白JNI的本质,还要从Java的本质说起。从本质上来说,Java语言的运行完全依赖于脚本引擎对Java的代码进行解释和执行。因为现代的Java可以从源代码编译成.class之类的中间格式的二进制文件,所以这种处理会加快Java脚本的运行速度。尽管如此,基本的执行方式仍然不变,由脚本引擎(被称之为JVM)来执行。与Python、Perl之类的纯脚本相比,只是把脚本变成了二进制格式而已。另外,Java本身就是一门对面向对象语言可以调用完善的功能库。当把这个脚本引擎移植到所有平台上之后,那么这个脚本就很自然地实现“跨平台”了。绝大多数的脚本引擎都支持一个很显著的特性,就是可以通过C/C++编写模块,并在脚本中调用这些模块。Java也同样如此,Java一定要提供一种在脚本中调用C/C++编写的模块的机制,才能称得上是一个相对完善的脚本引擎。
从本质上来看,Android平台是由arm-linux操作系统和一个叫做Dalvik的Java虚拟机组成的。所有在Android模拟器上看到的界面效果都是用Java语言编写的,具体请看源代码中的“frameworks/base”目录。由此可见,Dalvik只是提供了一个标准的支持JNI调用的Java虚拟机环境。在Android平台中,使用JNI技术封装了所有的和硬件相关的操作,通过Java去调用JNI模块,而JNI模块使用C/C++调用Android本身的arm-linux底层驱动,这样便实现了对硬件的调用。
在Android 4.4的源码中,和JNI相关的文件如下所示:
./frameworks/base/media/java/android/media/MediaScanner.java ./frameworks/base/media/jni/android_media_MediaScanner.cpp ./frameworks/base/media/jni/android_media_MediaPlayer.cpp ./frameworks/base/media/jni/AndroidRuntime.cpp ./libnativehelper/JNIHelp.cpp
由此可见,和JNI密切相关的是Media系统,而Media系统的架构基础是MediaScanner。在启动Android系统之初,就会扫描出系统中的Media文件供后续应用使用,既有新加入的媒体,也有几微秒种前删除的媒体文件,并且还需要自动更新相应的媒体库。在Android系统中,和用户体验密切相关的Music、Gallery播放等应用,也是基于MediaScanner的扫描媒体文件功能的。MediaScanner位于Android 4.4y源码的如下路径中:
packages/providers/MediaProvider
一个MediaScanner包含了3个主要部分:MediaScannerReceiver、MediaScannerService和MediaProvider。在“MediaProvider”目录下的AndroidManifest中可以查看MediaProvider的基本架构,如图4-2所示。
图4-2 MediaProvider的基本架构
MediaScannerReceiver:是一个BroadcastReceiver(接收广播),功能是进行媒体扫描,这也是MediaScanner提供给外界的接口之一。收到广播之后启动MediaScannerService具体执行扫描工作。
MediaScannerService:是一个Service,负责媒体扫描,它还要用到Framework中的MediaScanner来共同完成具体扫描工作,扫描的结果在MediaProvider提供的数据库中。
MediaProvider:是一个ContentProvider,媒体库(Images/Audio/Video/Playlist等)的数据提供者。负责操作数据库,并提供给别的程序insert、query、delete、update等操作。
在本章接下来的内容中,以MediaScanner源码分析作为基础,将详细分析JNI在Android系统中的作用。