4.3 分析JNI层
由于Android的应用层的类都是以Java写的,这些Java类编译为Dex型式的Bytecode之后,必须借助Dalvik虚拟机(Vitual Machine, VM)来执行并实现。VM在Android系统中扮演了一个很重要的角色,并且在执行Java类的过程中,如果Java类需要与C组件沟通时,VM就会去载入C组件,然后让Java的函数顺利地调用到C组件的函数。此时,VM扮演着桥梁的角色,让Java与C组件能通过标准的JNI介面而相互沟通。
应用层的Java类是在虚拟机上执行的,而C组件不是在VM上执行。如果Java程序又要求VM载入(Load)所指定的C组件,可以使用如下所示的指令实现这个功能:
System.loadLibrary(*.so的档案名);
例如,在Android框架里所提供的MediaPlayer.java类中包含了下面的指令:
public class MediaPlayer{ static { System.loadLibrary("media_jni"); } }
这要求VM去载入Android的/system/lib/libmedia_jni.so库。载入*.so后,Java类与*.so档案就汇合起来一起执行。
在JNI层中,MediaScanner的对应文件是:
./frameworks/base/media/jni/android_media_MediaScanner.cpp
在本节的内容中,将详细讲解JNI层的基本源码。
4.3.1 将Native对象的指针保存到Java对象
在文件android_media_MediaScanner.cpp中,函数android_media_MediaScanner_native_init的功能是将Native对象的指针保存到Java对象中。函数android_media_MediaScanner_native_init的具体实现代码如下所示:
static const char* const kClassMediaScanner = "android/media/MediaScanner"; ........ /*native_init函数的JNI层实现*/ 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", "I"); if (fields.context == NULL) { return; } }
4.3.2 创建Native层的MediaScanner对象
在文件android_media_MediaScanner.cpp中,函数android_media_MediaScanner_native_setup的功能是创建一个Native层的MediaScanner对象,但是此函数使用的是Opencore提供的PVMediaScanner。函数android_media_MediaScanner_native_setup的具体实现代码如下所示:
static void android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz) { ALOGV("native_setup"); MediaScanner *mp = new StagefrightMediaScanner; if (mp == NULL) { jniThrowException(env, kRunTimeException, "Out of memory"); return; } env->SetIntField(thiz, fields.context, (int)mp); }