騰訊面試官:知道什麼是Java Binder嗎?它是如何初始化的?


本文首發於微信公衆號「後廠技術官」html

關聯繫列
Android AOSP基礎系列
Android系統啓動系列
應用進程啓動系列
Android深刻四大組件系列
Android深刻理解Context系列
Android深刻理解JNI系列
Android解析WindowManager
Android解析WMS系列
Android解析AMS系列
Android包管理機制系列
Android輸入系統系列前端

原標題: Android Binder原理(六)Java Binder的初始化java

前言

Android Binder原理(一)學習Binder前必需要了解的知識點這篇文章中,我根據Android系統的分層,將Binder機制分爲了三層:android

  1. Java Binder (對應Framework層的Binder)
  2. Native Binder(對應Native層的Binder)
  3. Kernel Binder(對應Kernel層的Binder)

在此前的文章中,我一直都在介紹Native Binder和Kernel Binder的內容,它們的架構簡單總結爲下圖。程序員

MgRMbF.png

Android Binder原理(二)ServiceManager中的Binder機制這篇文章中,我講過BpBinder是Client端與Server交互的代理類,而BBinder則表明了Server端,那麼上圖就能夠改成: 算法

MgWuRI.png
從上圖能夠看到,Native Binder實際是基於C/S架構,Bpinder是Client端,BBinder是Server端,在 Android Binder原理(四)ServiceManager的啓動過程這篇文章中,咱們得知Native Binder經過ioctl函數和Binder驅動進行數據交互。 Java Binder是須要藉助Native Binder來進行工做的,所以Java Binder在設計上也是一個C/S架構,能夠說Java Binder是Native Binder的一個鏡像,因此在學習Java Binder前,最好先要學習此前文章講解的Native Binder的內容。本篇文章先來說解Java Binder是如何初始化的,即Java Binder的JNI註冊。

1.Java Binder的JNI註冊

Java Binder要想和Native Binder進行通訊,須要經過JNI,JNI的註冊是在Zygote進程啓動過程當中註冊的,代碼以下所示。
frameworks/base/core/jni/AndroidRuntime.cpp數組

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ...
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {//1
        return;
    }
    onVmCreated(env);
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
   ...
}
複製代碼

註釋1處用於啓動Java虛擬機,註釋2處startReg函數用於完成虛擬機的JNI註冊,關於AndroidRuntime的start函數的具體分析見Android系統啓動流程(二)解析Zygote進程啓動過程這篇文章。 startReg函數以下所示。 frameworks/base/core/jni/AndroidRuntime.cppbash

/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
    ATRACE_NAME("RegisterAndroidNatives");
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);

    ALOGV("--- registering native functions ---\n");
    env->PushLocalFrame(200);

    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {//1
        env->PopLocalFrame(NULL);
        return -1;
    }
    env->PopLocalFrame(NULL);
    return 0;
}
複製代碼

註釋1處的register_jni_procs函數的做用就是循環調用gRegJNI數組的成員所對應的方法,以下所示。微信

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數組中有100多個成員變量:架構

static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_com_android_internal_os_RuntimeInit),
    REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
    REG_JNI(register_android_os_SystemClock),
    ...
    REG_JNI(register_android_os_Binder),//1
   ...
};    
複製代碼

其中REG_JNI是一個宏定義:

#define REG_JNI(name) { name }
    struct RegJNIRec {
        int (*mProc)(JNIEnv*);
    };
複製代碼

實際上就是調用參數名所對應的函數。負責Java Binder和Native Binder通訊的函數爲註釋1處的register_android_os_Binder,代碼以下所示。
frameworks/base/core/jni/android_util_Binder.cpp

int register_android_os_Binder(JNIEnv* env) {   
    //註冊Binder類
    if (int_register_android_os_Binder(env) < 0)
        return -1;
    //註冊BinderInternal類 
    if (int_register_android_os_BinderInternal(env) < 0)
        return -1;
    //註冊BinderProxy類 
    if (int_register_android_os_BinderProxy(env) < 0)
        return -1;
    ...
    return 0;
}
複製代碼

register_android_os_Binder函數作了三件事,分別是: 1.註冊Binder類 2.註冊BinderInternal類 3.註冊BinderProxy類

它們是Java Binder關聯類的一小部分,它們的關係以下圖所示。

MTmhzd.png

  • IBinder接口中定義了不少整型的變量,其中定義一個叫作FLAG_ONEWAY的整形變量。客戶端發起調用時,客戶端通常會阻塞,直到服務端返回結果。設置FLAG_ONEWAY後,客戶端只須要把請求發送到服務端就能夠當即返回,而不須要等待服務端的結果,這是一種非阻塞方式。
  • Binder和BinderProxy實現了IBinder接口,Binder是服務端的表明,而BinderProxy是客戶端的表明。
  • BinderInternal只是在Binder框架中被使用,其內部類GcWatcher用於處理和Binder的垃圾回收。
  • Parcel是一個數據包裝器,它能夠在進程間進行傳遞,Parcel既能夠傳遞基本數據類型也能夠傳遞Binder對象,Binder通訊就是經過Parcel來進行客戶端與服務端數據交互。Parcel的實現既有Java部分,也有Native部分,具體實如今Native部分中。

下面分別對Binder、BinderInternal這兩個類的註冊進行分析。

1.1 Binder類的註冊

調用int_register_android_os_Binder函數來完成Binder類的註冊,代碼以下所示。 frameworks/base/core/jni/android_util_Binder.cpp

static const JNINativeMethod gBinderMethods[] = {
     /* name, signature, funcPtr */
    { "getCallingPid", "()I", (void*)android_os_Binder_getCallingPid },
    { "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
    { "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
    { "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
    { "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },
    { "getThreadStrictModePolicy", "()I", (void*)android_os_Binder_getThreadStrictModePolicy },
    { "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },
    { "getNativeBBinderHolder", "()J", (void*)android_os_Binder_getNativeBBinderHolder },
    { "getNativeFinalizer", "()J", (void*)android_os_Binder_getNativeFinalizer },
    { "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable }
};
const char* const kBinderPathName = "android/os/Binder";//1
static int int_register_android_os_Binder(JNIEnv* env) {
    jclass clazz = FindClassOrDie(env, kBinderPathName);//2

    gBinderOffsets.mClass = MakeGlobalRefOrDie(env, clazz);//3
    gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, "execTransact", "(IJJI)Z");//4
    gBinderOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");

    return RegisterMethodsOrDie(
        env, kBinderPathName,
        gBinderMethods, NELEM(gBinderMethods));
}
複製代碼

註釋1處的kBinderPathName的值爲"android/os/Binder",這是Binder在Java Binder中的全路徑名。 註釋2處根據這個路徑名獲取Binder的Class對象,並賦值給jclass類型的變量clazz,clazz是Java層Binder在JNI層的表明。 註釋3處經過MakeGlobalRefOrDie函數將本地引用clazz轉變爲全局引用並賦值給gBinderOffsets.mClass。 註釋4處用於找到Java層的Binder的成員方法execTransact並賦值給gBinderOffsets.mExecTransact。 註釋5處用於找到Java層的Binder的成員變量mObject並賦值給gBinderOffsets.mObject。 最後一行經過RegisterMethodsOrDie函數註冊gBinderMethods中定義的函數,其中gBinderMethods是JNINativeMethod類型的數組,裏面存儲的是Binder的Native方法(Java層)與JNI層函數的對應關係。

gBinderMethods的定義以下所示。

static struct bindernative_offsets_t {
    jclass mClass;
    jmethodID mExecTransact;
    jfieldID mObject;

} gBinderOffsets;
複製代碼

使用gBinderMethods來保存變量和方法有兩個緣由: 1.爲了效率考慮,若是每次調用相關的方法時都須要查詢方法和變量,顯然效率比較低。 2.這些成員變量和方法都是本地引用,在int int_register_android_os_Binder函數返回時,這些本地引用會被自動釋放,所以用gBinderOffsets來保存,以便於後續使用。

對於JNI不大熟悉的同窗能夠看Android深刻理解JNI(二)類型轉換、方法簽名和JNIEnv這篇文章。

1.2 BinderInternal類的註冊

調用int_register_android_os_BinderInternal函數來完成BinderInternal類的註冊,代碼以下所示。 frameworks/base/core/jni/android_util_Binder.cpp

const char* const kBinderInternalPathName = "com/android/internal/os/BinderInternal";
static int int_register_android_os_BinderInternal(JNIEnv* env) {
    jclass clazz = FindClassOrDie(env, kBinderInternalPathName);

    gBinderInternalOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gBinderInternalOffsets.mForceGc = GetStaticMethodIDOrDie(env, clazz, "forceBinderGc", "()V");
    gBinderInternalOffsets.mProxyLimitCallback = GetStaticMethodIDOrDie(env, clazz, "binderProxyLimitCallbackFromNative", "(I)V");

    jclass SparseIntArrayClass = FindClassOrDie(env, "android/util/SparseIntArray");
    gSparseIntArrayOffsets.classObject = MakeGlobalRefOrDie(env, SparseIntArrayClass);
    gSparseIntArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseIntArrayOffsets.classObject,
                                                           "<init>", "()V");
    gSparseIntArrayOffsets.put = GetMethodIDOrDie(env, gSparseIntArrayOffsets.classObject, "put",
                                                   "(II)V");

    BpBinder::setLimitCallback(android_os_BinderInternal_proxyLimitcallback);

    return RegisterMethodsOrDie(
        env, kBinderInternalPathName,
        gBinderInternalMethods, NELEM(gBinderInternalMethods));
}
複製代碼

和int_register_android_os_Binder函數的實現相似,主要作了三件事: 1.獲取BinderInternal在JNI層的表明clazz。 2.將BinderInternal類中有用的成員變量和方法存儲到gBinderInternalOffsets中。 3.註冊BinderInternal類的Native方法對應的JNI函數。

還有一個BinderProxy類的註冊,它和Binder、BinderInternal的註冊過程差很少,這裏就再也不贅述了,有興趣的讀者能夠自行去看源碼。

更多的內容請關注個人獨立博客的知識體系:
liuwangshu.cn/system/


分享Java、Python、大數據、算法、大前端、AI相關技術,關注程序員技術提高和職場晉升,助力10W+程序員進階爲技術官和架構師!

相關文章
相關標籤/搜索