一、JNI概述
JNI是Java Native Interface的缩写,中文译为“Java 本地调用”。通俗地说,JNI是一种技术,通过这种技术可以做到以下两点:
Java程序中的函数可以调用Native语言写的函数,Nativie一般指的是C/C++编写的函数。
Native程序中的函数可以调用Java层的函数,也就是说在C/C++程序中可以调用Java函数。
在平台无关的Java中,为什么要创建一个与Native相关的JNI技术呢?这岂不是破坏了Java的平台无关特性吗?JNI技术的推出有以下几个方面的考虑。
承载Java世界的虚拟机是Native语言写的。而虚拟机又运行在具体的平台上,所有虚拟机本身无法做到平台无关。然而,有了JNI技术后就可以对Java层屏蔽不同的操作系统平台之间的差异了。这样,就能实现Java本身的平台无关特性。其实Java一种使用JNI技术,只是我们平时较少用到罢了。
早在Java语言诞生前,很多程序都是用Native语言写的,它们遍布在世界各地。Java出世后,它受到了追捧,并迅速得到发展,但仍无法将软件世界彻底改朝换代,于是才有了折中的办法。既然已经有Native模块实现了相关功能,那么在Java中通过JNI技术直接使用它们就行了,防止重复造轮子。
在Android平台上,JNI就是一座将Native世界和Java世界间相连接的桥梁。
二、学习JNI的实例:MediaScanner
由于Android大量使用了JNI技术,本章接下来的内容就将源码中的一处实例来讲解JNI相关的知识。
Java(MediaScanner)——》JNI(libmedia_jni.so)——》Native(libmedia.so)
Java世界对应的是MediaScanner,而这个MediaScanner类有一些函数需要由Native层来实现。
JNI层对应的是libmedia_jni.so。media_jni是JNI库的名字,其中,下划线前的“media”是Native层库的名字,这里就是libmedia库。下划线后的“jni”表示它是一个JNI库。注意,JNI库的名字可以随便取,不过Android平台基本上都采用“lib模块名_jni.so”命名的方式。
Native层对应的是libmedia.so,这个库完成了实际的功能。
Media
Java层的MediaScanner分析
先来看MediaScanner的源码,这里将提取出与JNI相关的部分
加载JNI库
前面说过,如果Java要调用native函数,就必须通过一个位于JNI层的动态库来实现。顾名思义,动态库就是运行时加载的库,那么在什么时候以及什么地方加载这个库呢?
这个问题没有标准答案,原则上是:在调用native函数前,任何时候、任何地方加载都可以。通行的做法是在类的static语句中加载,调用System.loadLibrary方法就可以了。
这一点在上面的代码也见到了。另外,System.loadLibrary函数的参数是动态库的名字,即media_jni。系统会自动根据不同的平台扩展成真实的动态库文件名,例如在Linux系统上会拓展成libmedia_jni.so,而在Window平台上就会拓展成media_jni.dll。
解决了JNI库加载的问题
Java的native函数和总结
从上面代码中可以发现,native_init和processFile函数前都有Java的关键子native,它表示这两个函数将由JNI层来实现。
Java层的分析到此结束。JNI技术也很照顾Java程序员,只要完成下面两项工作就可以使用JNI了:
加载对应的JNI库
声明由关键字native修饰的函数
所有,对于Java程序员来说,使用JNI技术真的是太容易了。不过JNI层要完成任务可没那么轻松,下面来看对JNI层的MediaScanner的分析
2.4JNI层MediaScanner的分析
注册JNI函数
正如代码中注释的那样,native_init函数对应的JNI函数是android_media_MediaScanner_native_init,可能细心的读者要问了,你怎么知道native函数对应的是这个JNI函数,而不是其他的呢?莫非是根据函数的名字来确定的?