深入解析Android 虚拟机
上QQ阅读APP看书,第一时间看更新

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);
        }