JNI函数的注册方法

来源:互联网 发布:东莞好玩的地方知乎 编辑:程序博客网 时间:2024/05/17 12:21

摘录、参考文档:

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

上一篇 讲了关于JNI技术的一些基础知识。

现在讲一下JNI函数的注册方法。

注册JNI函数

JNI函数的注册方法实际上有两种,静态注册、动态注册。下面将分别就这两种方法做一下介绍。

1.静态注册

这种方法就是根据函数名来找对应的JNI函数。它需要java的工具程序javah参与,整体流程如下:

1)先编写java代码,生成.class文件;

2)使用java的工具程序javah,如 javah -o output packagename.classname,这样它会生成一个叫output.h的JNI层头文件。其中packagename.classname是java代码编译后的class文件,而在生成的output.h文件中,声明了对应的JNI层函数,只要实现里面的函数即可。

这个JNI的头文件,一般都会使用packagename_class.h的样式,例如MediaScanner对应的JNI层头文件就是android_media_MediaScanner.h。下面来看看这种方式生成的头文件:



这里解释一下静态注册方法中,java中的native函数是如何找到JNI中对应的函数的。其实,过程非常简单:

当java层调用native_init函数时,它会从对应的JNI库中寻找 Java_android_media_MediaScanner_native_linit函数,如果没有,就会报错。如果找到,则会为native_init函数和Java_android_media_MediaScanner_native_linit函数建立一个关联关系,其实就是保存JNI层函数的函数指针。以后再调用native_init函数时,直接使用这个函数指针就可以了,当然这项工作是由虚拟机完成的。

从这里可以看出,静态方法就是根据函数名来建立java函数和JNI函数之间的关联关系的,而且它要求JNI层函数的名字必须遵守特定的格式。这种方法也有几个弊端,即:

1)需要编译所有声明了native函数的java类,每个所生成的class文件都得用javah生成一个头文件;

2)javah生成的JNI层函数名特别长,书写起来很不方便;

3)初次调用native函数时,要根据函数名称搜索对应的JNI层函数来建立关联关系,这样会影响运行效率。

有什么办法可以克服上面三个弊端吗?根据上面的介绍可知,java native函数是通过函数指针来和JNI层函数jian'li关联关系的。如果直接让native函数知道JNI层对应函数的函数指针,不就万事大吉了吗?这就是下面介绍的第二种注册方法:动态注册法。

2.动态注册

在JNI技术中,用来记录java native函数和JNI函数这种一一对应关系的,是一个叫JNINativeMethod的结构,其定义如下:

这个结构体又是怎么使用的呢?看看MediaScanner JNI层是如何做的,代码如下所示:

AndroidRuntime类提供了一个registerNativeMethods函数来完成注册工作,下面来看看这个函数的代码:

其中,jniRegisterNativeMethods函数是Android平台为了方便JNI使用而提供的一个帮助函数,其代码如下所示:

其实动态注册的工作,只需要两个函数就能完成,代码如下:

所以,在自己的JNI层代码中使用这种方法,就可以完成动态注册了。但是上面讲了这么多,这些动态注册的函数实在什么时候,什么地方调用的呢?

当java层通过System.loadLibrary函数加载完JNI动态库之后,紧跟着会查找该库中一个叫JNI_OnLoad的函数。如果有,就调用它,而动态注册的工作就是在这里完成的。

所以,如果想使用动态注册方法,就必须实现JNI_OnLoad函数,只有在这个函数中才有机会完成动态注册的工作。静态注册则没有这个要求,但建议大家实现这个函数,因为有一些初始化工作可以放在这个函数里面进行。

那么,libmedia_jni.so的JNI_OnLoad函数是在那里实现的咧?由于多媒体系统很多地方都用到了JNI,所以“码农”把它放到android_media_MediaPlayer.cpp中了,代码如下所示:

JNI函数注册的相关内容就介绍完了。

注意:

JNI层代码中一般要包含jni.h这个头文件。Android源码中提供了一个帮助头文件JNIHelp.h,它内部其实就包含了jni.h,所以我们在自己的代码中直接包含这个JNIHelp.h即可。

下面的文章,将会讲解JNI技术中的其它几个重要部分。



0 0
原创粉丝点击