Android中,JNI技术的相关基础知识

来源:互联网 发布:人造奶油危害 知乎 编辑:程序博客网 时间:2024/05/21 06:35

摘录、参考文档:

1.深入理解Android:卷1,作者邓凡平


JNI概述

JNI是Java Native Interface的缩写,中文译为“java本地调用”。通俗的说,JNI是一种技术,通过这种技术可以坐到下面两点:

1)java程序的函数调用native层的函数,native大部分指c/c++编写的函数;

2)native程序的函数,调用java层的函数。

为什么会出现JNI技术呢?这样不是破坏了java的平台无关特性吗?JNI技术的推出主要有下面两点考虑:

1)承载java世界的虚拟机使用native(c/c++)写的,而虚拟机运行在具体的平台上(比如linux上面),虚拟机本身无法做到平台无关性。没有JNI技术,java程序调用底层API时,需要对各个平台的差异进行兼容,无法实现java的平台无关特性。然而,有了JNI技术后,就可以对java层屏蔽不同系统之间的差异了(比如同样打开一个文件,win上的API使用的是OpenFile函数,二Linux上使用的是open函数),这样,通过抽象,给java层提供API,实现了java的平台无关特性。

2)java诞生之前,很多程序都是用从c/c++写的,它们已经遍布软件时间的各个角落。java出现之后,收到热捧,但是无法将软件时间改朝换代,于是有了折中的办法。既然已经有native实现了相关的功能,就可以避免重复造轮子,直接使用java的JNI技术调用它们就可以了。另外,在追求效率和速度的地方,c/c++比java实现要更好。

在Android平台上,JNI就是一座将native世界和java世界间的天堑变成通途的桥。


在书中,是以MediaScanner源码为例子带入,进行讲解JNI的实现的,我将摘录一些重要知识点记录下来。


从上图可以看到:

1)java对应的是MediaScanner,而这个MediaScanner中有一些函数功能需要native层来实现;

2)JNI层对应的是libmedia_jni.so,media_jni时JNI库的名字,其中,下划线前的“media”是Native层库的名字,这里就是libmedia库。下划线后的“jni”表示它是一个JNI库。注意,JNI库的名字可以随便取,不过Android平台基本上采用的是“lib模块名_jni.so”的命名方式;

3)Native层对应的是libmedia.so,这个库完成了实际的功能;

4)MediaScanner类将通过JNI库libmedia_jni.so和Native层的libmedia.so进行交互。

从上面的分析中还可知道,JNI层必须实现为动态库的形式,这样java虚拟机才能加载并使用它的函数。

java层的MediaScanner类分析


上面代码片段中有两个比较重要的点:加载JNI库;java中的native函数。

java要调用native函数,就必须通过一个位于JNI层的动态库来实现。顾名思义,动态库就是运行时加载的库。什么时候以及什么地方加载这个库合适呢?

这个问题没有标准答案,原则上是:在调用native函数之前,任何时候、任何地方加载都可以。通行的做法是放在类的static块中加载,调用System.loadLibrary函数就可以了。MediaScanner类就是这么干的,我们也可以这么写。另外,System.loadLibrary函数的参数是动态库的名字,即media_jni。系统会根据不同的平台扩展成真实的动态库名称,例如在linux系统上会扩展成libmedia_jni.so,而在windows平台上则会扩展成media_jni.dll。

java的native函数和总结

从上面的代码可以看出,native_jni和processFile函数前都有java的关键字native修饰,它表示这两个函数将由JNI层实现。

java层的分析到此结束。JNI技术也很照顾java程序员,只要完成下面两项工作就可以使用JNI了:

1)加载对应的JNI库;

2)声明由关键字native修饰的函数。

JNI层MediaScanner的分析


这里我们可能都有一些疑惑,可以其中一个疑惑就是,如何才能知道java层的native_java函数对应的是JNI层的android_media_MediaScanner_native_init函数呢?下面的篇幅就是回答这个问题。

注册JNI函数

正如代码注释的一样,java层的native_java函数对应的是JNI层的android_media_MediaScanner_native_init函数,那么这又是怎么关联的呢?

大家知道,MediaScanner类位于android.media包中,native_init在MediaScanner类中,所以native_init的全路径应该是android.media.MediaScanner.native_init,而JNI层函数的名字是android_media_MediaScanner_native_init。因为在native语言中,符号“.”有着特殊的含义,所以JNI层需要把java函数名称,包括包名中的"."换成“_”。也就是通过这种方式,native_init找到了JNI层的实现,android_media_MediaScanner_native_init。

上面其实讨论的是JNI函数的注册问题,“注册”之意就是将java层的native函数和JNI层对应的函数关联起来,有了这种关联,调用java层的native函数时,就能顺利转到JNI层对应的函数执行了。而JNI函数的注册方法实际上有两种,静态注册、动态注册。

下文将会讲解静态注册、动态注册是如何进行的。




0 0
原创粉丝点击