Native方法注册分为两种:
- 静态注册
多用于NDK开发
- 动态注册
多用于Framework层开发
下面我们用实际的例子对两种注册做区分及了解。
1.静态注册
我们在Android Studio一个项目中
右键本项目->new->module->Java Library
LibraryName = media
Java class name = MediaRecorder.java,创建成功后,打开MediaRecorder.class
,并学着MediaRecorder编写:
接着我们对MediaRecorder.java
进行编译和生成JNI方法,我们进入 media/src/main/java中,在 terminal命令行中输入:
javac com/rikkatheworld/media/MediaRecorder.java
javah -jni com.rikkatheworld.media.MediaRecorder
最后会产生了一个 MediaReorder.class
和一个 com_rikkatheworld_media_MediaRecorder.h
头文件。
上面可能这里可能会有点坑,可以看我 Android音视频开发入门(5)使用LAME编码一个PCM文件第一节怎么去搭建一个JNI项目,这里我就不多赘述。
我们来看下这个被编译出来的头文件的内容:
// com_rikkatheworld_media_MediaRecorder.h
#include <jni.h>
#ifndef _Included_com_rikkatheworld_media_MediaRecorder
#define _Included_com_rikkatheworld_media_MediaRecorder
#ifdef __cplusplus
extern “C” {
#endif
// 1
JNIEXPORT void JNICALL Java_com_rikkatheworld_media_MediaRecorder_native_1init
(JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_com_rikkatheworld_media_MediaRecorder_start
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
被编译出来的头文件就是我们Java层在JNI层中对应的类。
注释1中的方法:以Java开头,说明这个方法在Java中调用JNI方法的,后面的名称是以 包名+类名+方法名组成格式,还发现 native_1init()
中多了个“1”,这是因为Java声明的native_init()
中有 “_”下划线,转换成到native语言中,就变成了 “_1”。
其中 JNIEnv
是Native世界中 Java环境的代表,通过 JNIEnv *
这个指针,就可以在Native层中访问Java层的代码并进行操作了,它只在创建它的线程中有效,不能跨线程传递。
jclass
是JNI型数据,对应Java的 java.lang.Class的实例,是所有对象的父类。
上面这两个数据类型是JNI层常用且关键的数据,后面会对其做介绍。
我们在 Java层中调用 native_init()
后,就会从 JNI中寻找 Java_com_rikkatheworld_media_MediaRecorder_native_1init()
的函数,如果没有,就会报错,如果找到就会为这两个方法建立一个关联,它就会保存一个JNI的函数指针,即一个指针指向了这个函数,等下一次再调用这个方法时,这个指针所指向的函数就能立马被调用。这就是静态注册。
静态注册总结:
根据方法名,将Java方法和JNI函数建立联系,它的缺点是:
-
JNI层的函数名非常长,它是
Java+包名+类名+方法名
-
声明Native方法的类需要用 javah生成头文件
-
第一次调用时,由于JNI层的指针还未指向对应函数,所以它要一个一个找直到找到,所以每第一次调用新的Native方法时,效率并不会太高
2.动态注册
静态注册是Java的Native通过方法指针来与JNI进行关联的,如果Java的Native方法一开始就知道它在JNI中对应的函数指针,就可以避免上述的缺点,这就是动态注册。
JNI中有一种结构来记录Java的Native方法和JNI方法的关联联系,它就是 JNINativeMethod
,它在jni.h
中的源码是这样的:
// jni.h
typedef struct {
const char* name; //Java方法中的名字
const char* signature; //Java方法的简明信息
void* fnPtr; //JNI中对应的方法指针
} JNINativeMethod;
我们应用开发中使用的 MediaRecorder就是采用动态注册的方式,我们来看看其JNI层是怎么来使用这个 JNINativeMethod
的:
// android_media_MediaRecorder.cpp
static const JNIN