JNI—阅读Android源码

终于来到解决最后一个问题"如何阅读Android Native 源码?"了,也将是JNI系列最后一篇文章:
JNI—NDK开发流程(ndk-build与CMake)
JNI—C/C++与Java的交互
JNI—阅读Android源码

主要解决三个问题:

NDK的开发流程?
C/C++ 与 Java如何进行通信的?
如何阅读Android Native 源码?

当初学习JNI仅仅只是为了读懂Android Native源码,没想到整了那么多(手动苦笑),那么一起来涨涨阅读Android Native源码的姿势。
本文基于Android P源码。

1. 概述

之前在阅读Android系统源码的过程中发现大量的Native的调用,不熟悉的情况下阅读起来非常困难,所以下定决心整明白其中的关系。下面将讲述如何查找Android API中native方法的源码文件,以及它们是怎么关联起来的。其根本都是使用动态注册的方式(详情可阅读上一篇文章)

2. 查找源码

以InputManagerService.java 以及 Bitmap.java为例。

2.1. [类的全限定名].cpp

IMS(InputManagerService)分为两层JAVA层和Native层,在InputManagerService.java 里面有大量native方法。

InputManagerService.java

    .....
    private static native long nativeInit(InputManagerService service,
            Context context, MessageQueue messageQueue);
    private static native void nativeStart(long ptr);
    private static native void nativeSetVirtualDisplayViewports(long ptr,
            DisplayViewport[] viewports);
    private static native void nativeSetDisplayViewport(long ptr, int viewportType,
            int displayId, int rotation,
            int logicalLeft, int logicalTop, int logicalRight, int logicalBottom,
            int physicalLeft, int physicalTop, int physicalRight, int physicalBottom,
            int deviceWidth, int deviceHeight, String uniqueId);

    .....

那么怎么定位到其本地方法的实现呢?有人说可以全局搜索方法名(nativeInit、nativeStart),但是发现会出现大量的匹配项,而且搜索效率奇低。其实Andriod系统对于native源码文件有一个常用的命名规则:[类的全限定名].cpp,虽然非百分百遵守这个规则,但是我们可以利用该规则来进行“碰瓷”式查找。
比如InputManagerService.java的类全限定名为com.android.server.input.InputManagerService,对于的JNI源文件为com_android_server_input_InputManagerService.cpp。 在AS下双击shift键搜索:
JNI—阅读Android源码_第1张图片
一下子就找到其本地源码(frameworks\base\services\core\jni),再看看该目录下的本地源码文件都遵循该命名规则,也就是说可以利用该规则碰瓷式查找源码:
JNI—阅读Android源码_第2张图片

2.2. 搜索“类文件目录”

前面也说到过2.1的方法不一定有效,比如Bitmap.java,搜索android_graphics_Bitmap.cpp无法查找出对应的源文件。那该怎么办?还记得动态注册的方式么,动态注册时需要通过类的全限定名查找JAVA类,那么可以利用类的全限定名字符串定位相关源码文件。比如Bitmap对应的类文件目录"android/graphics/Bitmap",全局搜索结果如下:
JNI—阅读Android源码_第3张图片
一下子就定位出Bitmap对应的本地源码文件(\frameworks\base\core\jni\android\graphics\Bitmap.cpp),然后爱怎么阅读就怎么阅读了。当然也可以“碰瓷”地搜索“Bitmap.cpp”,搜索查找"android/graphics/Bitmap"条件更优。

3. 系统Native如何关联JNI方法

JAVA方法与本地方法关联起来,无异于两种方式:静态注册和动态注册。而Android API与本地方法关联往往使用动态注册的方式。还是以InputManagerService.java 以及 Bitmap.java为例。

3.1. 系统服务本地方法的动态注册

在InputManagerService.java源码文件中没有发现任何地方调用System.loadLibrary()。那是不是系统API就不需要加载了?非也非也,尝试通过反向推导的方式找出Service 本地方法的注册的地方。

  • 查看InputManagerService本地源码文件,找到动态注册的函数,然后找到谁调用了它。

    com_android_server_input_InputManagerService.cpp

    int register_android_server_InputManager(JNIEnv* env) {
        int res = jniRegisterNativeMethods(env, "com/android/server/input/InputManagerService",
                gInputManagerMethods, NELEM(gInputManagerMethods));
        ......
    }
    
  • 发现在onload.app(frameworks\base\services\core\jni/)被调用,发现了我们非常熟悉的JNI_OnLoad()的方法,感觉离真相越来越近了。

    onload.app

    extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
    {
        .....
        register_android_server_InputApplicationHandle(env);
        register_android_server_InputWindowHandle(env);
        register_android_server_InputManager(env);
        .....
    }
    
  • 获取其so动态库名称libservices.core看看谁l对它执行loadLibrary(),查看onload.app文件目录下的Android.bp文件(在Android高版本中使用Android.bp作编译配置文件,废弃了Android.mk)。

    Android.bp

    cc_library_static {
        name: "libservices.core",
        defaults: ["libservices.core-libs"],
        .....
        srcs: [
            .....
            "onload.cpp",
        ],
        .....
    }
    
  • 全局搜索看看谁loadLibrary(“libservices.core”),相当失望没有任何JAVA代码去load该so库。线索中断了?!等等,但是有一个搜索结果有着重大嫌疑。frameworks\base\services\Android.bp 配置文件的whole_static_libs: [“libservices.core”]:
    JNI—阅读Android源码_第4张图片

    Android.bp

    java_library {
        .......
        cc_library_shared {
            name: "libandroid_servers",
            defaults: ["libservices.core-libs"],
            whole_static_libs: ["libservices.core"],
        }    
    }
    
    

    那么whole_static_libs是什么意思呢?在soong_build中查询发现,该意思需要链接该模块列表。即加载libandroid_servers so库时也会加载链接libservices.core 库。

  • 最后发现SystemServer的run()方法中加载了,SystemServer.main()在系统启动的时候会被调用然后执行run()方法,到这里基本已经找到系统服务service本地方法关联的时机和地方了。
    SystemServer.java

        /**
         * The main entry point from zygote.
         */
        public static void main(String[] args) {
            new SystemServer().run();
        }
    
        private void run() {
            ...
            // Initialize native services.
            System.loadLibrary("android_servers");
            ...
        }
    
  • 小结
    系统服务的native方法在系统启动时SystemServer.run()中调用System.loadLibrary(“android_servers”)进行关联起来。

3.2. Zygote启动时的动态注册

然而对于Bitmap等非系统服务类的本地方法是如何关联的呢?——Zygote启动过程中。Zygote启动的过程中会执行AndroidRuntime.cpp中几个函数方法:

int AndroidRuntime::startReg(JNIEnv* env)

static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env) 
  • 主要关注register_jni_procs,不深入每个函数的细节了。

    AndroidRuntime.cpp

    static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
    {
        for (size_t i = 0; i < count; i++) {
            if (array[i].mProc(env) < 0) {
            #ifndef NDEBUG
                    ALOGD("----------!!! %s failed to load\n", array[i].mName);
            #endif
                    return -1;
                }
            }
            return 0;
        }
    
  • 其中gRegJNI数组存储了Android API各模块注册的函数指针的结构体RegJNIRec,即动态注册都在各个模块自身中实现

    AndroidRuntime.cpp

    #ifdef NDEBUG
        #define REG_JNI(name)      { name }
        struct RegJNIRec {
            int (*mProc)(JNIEnv*);
        };
    #else
        #define REG_JNI(name)      { name, #name }
        struct RegJNIRec {
            int (*mProc)(JNIEnv*);
            const char* mName;
        };
    #endif
    .....
    static const RegJNIRec gRegJNI[] = {
        ....
        REG_JNI(register_android_graphics_Bitmap),
        REG_JNI(register_android_graphics_BitmapFactory),
        REG_JNI(register_android_graphics_BitmapRegionDecoder),
        .....
    }
    
  • Bitmap.cpp实现动态注册。
    Bitmap.cpp

    int register_android_graphics_Bitmap(JNIEnv* env)
    {
        gBitmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap"));
        gBitmap_nativePtr = GetFieldIDOrDie(env, gBitmap_class, "mNativePtr", "J");
        gBitmap_constructorMethodID = GetMethodIDOrDie(env, gBitmap_class, "", "(JIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V");
        gBitmap_reinitMethodID = GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V");
        gBitmap_getAllocationByteCountMethodID = GetMethodIDOrDie(env, gBitmap_class, "getAllocationByteCount", "()I");
        return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods,
                                             NELEM(gBitmapMethods));
    }
    
  • 动态注册的调用,最终又回到AndroidRuntime.cpp执行jniRegisterNativeMethods()函数,然后进入JNI工具类JNIHelp.cpp中执行jniRegisterNativeMethods,这个函数就是典型的动态注册函数
    core_jni_helpers.h

    static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className,
                                           const JNINativeMethod* gMethods, int numMethods) {
        int res = AndroidRuntime::registerNativeMethods(env, className, gMethods, numMethods);
        LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
        return res;
    }
    

    AndroidRuntime.cpp

    /*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
        const char* className, const JNINativeMethod* gMethods, int numMethods)
    {
        return jniRegisterNativeMethods(env, className, gMethods, numMethods);
    }
    

    JNIHelp.cpp

    extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
        const JNINativeMethod* gMethods, int numMethods)
    {
        JNIEnv* e = reinterpret_cast(env);
    
        ALOGV("Registering %s's %d native methods...", className, numMethods);
    
        scoped_local_ref c(env, findClass(env, className));
        if (c.get() == NULL) {
            char* tmp;
            const char* msg;
            if (asprintf(&tmp,
                         "Native registration unable to find class '%s'; aborting...",
                         className) == -1) {
                // Allocation failed, print default warning.
                msg = "Native registration unable to find class; aborting...";
            } else {
                msg = tmp;
            }
            e->FatalError(msg);
        }
    
        if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
            char* tmp;
            const char* msg;
            if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
                // Allocation failed, print default warning.
                msg = "RegisterNatives failed; aborting...";
            } else {
                msg = tmp;
            }
            e->FatalError(msg);
        }
    
        return 0;
    }
    

4. 涉及的源码文件及其JNI常见目录:

涉及的源码:

frameworks\base\core\jni\AndroidRuntime.cpp
frameworks\base\services\core\jni\onload.cpp
frameworks\base\core\jni\core_jni_helpers.h
libnativehelper\JNIHelp.cpp
libnativehelper\include\nativehelper\JNIHelp.h

frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp

frameworks\base\graphics\java\android\graphics\Bitmap.java
frameworks\base\core\jni\android\graphics\Bitmap.cpp

JNI常见的目录:

frameworks\base\core\jni\
frameworks\base\services\
framework\base\media\jni\
libnativehelper\
libcore\

5. 总结

其实Android API与本地方法的关联最终也是通过静态注册或动态注册的方式,在阅读源码的过程中,可通过**[类的全限定名].cpp搜索方式进行初步搜索,如果找不到直接搜索“类文件目录”**,一般可以定位出对应JNI源码文件。最后想说,基础很重要,希望后面自己继续努力。

你可能感兴趣的:(Android)