sunwengang blog

developer | android display/graphics

  1. 1. 概述
  2. 2. MediaScanner示例
    1. 2.1. Java层的MediaScanner
    2. 2.2. JNI层的MediaScanner
      1. 2.2.1. 注册JNI函数

概述

JNI,即Java Native Interface,Java本地调用。通过JNI可以实现:

  • Java程序函数可以调用Natvie语言(C/C++)写的函数
  • Natvie程序函数可以调用Java层的函数

MediaScanner示例

使用Android Xref提供的 Android 9.0.0_r3的源码

Java层的MediaScanner

完成两件事:

  1. 加载JNI库
  2. Java的native函数
frameworks/base/media/java/android/media/MediaScanner.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MediaScanner implements AutoCloseable {
static {
System.loadLibrary("media_jni"); //加载对应的JNI库,media_jni是库名,在实际加载动态库的时候会拓展成libmedia_jni.so
native_init(); //调用函数
}

private final static String TAG = "MediaScanner";
...
private native void processDirectory(String path, MediaScannerClient client);
private native boolean processFile(String path, String mimeType, MediaScannerClient client);
private native void setLocale(String locale);

public native byte[] extractAlbumArt(FileDescriptor fd);
private static native final void native_init();
private native final void native_setup();
private native final void native_finalize();

动态库是运行时加载的库。如果Java要调用native函数,必须通过一个位于JNI层的动态库实现。通常是在类的static语句中加载,调用System.loadLibrary方法就可以加载。

函数名前有Java的关键字native的函数表示将由JNI层实现。

因而Java层只需要两项工作:加载对应的JNI库,和声明由关键字native修饰的函数

JNI层的MediaScanner

JNI对应的文件是frameworks/base/media/jni/android_media_MediaScanner.cpp

frameworks/base/media/jni/android_media_MediaScanner.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// This function gets a field ID, which in turn causes class initialization.
// It is called from a static block in MediaScanner, which won't run until the
// first time an instance of this class is used.
static void
android_media_MediaScanner_native_init(JNIEnv *env)
{
ALOGV("native_init");
jclass clazz = env->FindClass(kClassMediaScanner);
if (clazz == NULL) {
return;
}

fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.context == NULL) {
return;
}
}
...

java层的native_init函数对应android_media_MediaScanner_native_init。通过文件路径来命名,观察两个文件的路径:

  • frameworks/base/media/java/android/media/MediaScanner.java
  • frameworks/base/media/jni/android_media_MediaScanner.cpp

注册JNI函数

java层的MediaScanner.java函数native_init位于android.media包中,全路径名是:android/media/MediaScanner.java,对应JNI层函数的名字。JNI层将Java函数名称(包含包名)中的.转换成_,通过这种方式,native_init对应JNI的函数。

JNI函数注册的意思是将java层的native函数和JNI层对应的实现函数关联起来。注册有两种方式:静态方法和动态注册

  1. 静态注册

根据函数名来找对应的JNI函数。

(1) 编写Java代码,编译生成.class文件

(2) 使用Java的工具命令javah -o output packagename.classname,生成一个output.h的JNI头文件,里面声明了对应的JNI函数,只要实现里面的函数即可。

  1. 动态注册

因为Java native函数和JNI函数是一一对应的,所以存在一种JNINativeMethod的结构记录这种一一对应的关系。

例如frameworks/base/media/jni/android_media_MediaScanner.cpp文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
static const JNINativeMethod gMethods[] = {
......
{
"native_setup",
"()V",
(void *)android_media_MediaScanner_native_setup
},

{
"native_finalize",
"()V",
(void *)android_media_MediaScanner_native_finalize
},
};
//注册上面的数组
// This function only registers the native methods, and is called from
// JNI_OnLoad in android_media_MediaPlayer.cpp
int register_android_media_MediaScanner(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env,
kClassMediaScanner, gMethods, NELEM(gMethods));
}

当Java层通过System.loadLibrary加载完JNI动态库后,会查找该库中JNI_OnLoad函数,然后调用他,之后完成动态注册。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
JNIEnv* env = NULL;
jint result = -1;

if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("ERROR: GetEnv failed\n");
goto bail;
}
assert(env != NULL);

......
if (register_android_media_MediaRecorder(env) < 0) {
ALOGE("ERROR: MediaRecorder native registration failed\n");
goto bail;
}

if (register_android_media_MediaScanner(env) < 0) { //此处开始动态注册
ALOGE("ERROR: MediaScanner native registration failed\n");
goto bail;
}

if (register_android_media_MediaMetadataRetriever(env) < 0) {
ALOGE("ERROR: MediaMetadataRetriever native registration failed\n");
goto bail;
}
......

/* success -- return valid version number */
result = JNI_VERSION_1_4;

bail:
return result;
}

本文作者 : sunwengang
本文使用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议
本文链接 : https://alonealive.github.io/Blog/2019/08/29/2019/190829_android_JNI/

本文最后更新于 天前,文中所描述的信息可能已发生改变