深入理解android-jni,binder,zygote,ams


title: 深入理解android-jni,binder,zygote,ams
date: 2020-02-25 15:48:23
tags: [android framework jni binder]

typora-copy-images-to: upload
typora-root-url: ./深入理解android


jni

Jni就是java代碼與Native(c,c++)代碼交互的媒介. 通過jni,java代碼可以主動調(diào)用native代碼,然后native代碼在被調(diào)用時,可以回調(diào)java的代碼.

jni由兩部分構(gòu)成. Java端的聲明為 native 的java方法.和native端對應的Java方法的實現(xiàn)方法. 然后就是通過一些技術(shù)把兩端的聲明方法和實現(xiàn)方法建立映射關(guān)系.就可以調(diào)用了.

jni分為靜態(tài)注冊和動態(tài)注冊.

靜態(tài)注冊就是由java端聲明的native的java方法生成固定命名規(guī)則的native方法的頭文件.然后native端實現(xiàn)這些頭文件的方法. 在把native端代碼打成so包.java端在調(diào)用聲明native的java方法前加載so包.然后執(zhí)行native 的java方法.系統(tǒng)就會在so包找找固定命名規(guī)則的native方法進行調(diào)用.

靜態(tài)注冊的話,每個java類的native方法都會生成一個固定的.h文件.

動態(tài)注冊就是對java端聲明的native方法和native端的方法進行動態(tài)的綁定(當然這兩個方法的形參得匹配),然后保存在一個native端的JNINativeMethod的結(jié)構(gòu)體中,然后在JNI_OnLoad方法中注冊這個匹配關(guān)系,java中先加載這個native方法生成的so庫,然后在java代碼執(zhí)行native聲明的方法時,從so庫中查找JNINativeMethod機構(gòu)提的匹配規(guī)則,找到對應native中的方法,執(zhí)行.

動態(tài)注冊的話,可以為多個java類中聲明的所有native方法聲明一個native端實現(xiàn)文件,并且不需要頭文件.

靜態(tài)注冊

java端native方法如下

public static native String getAppKey();  加入native關(guān)鍵字,不需要實現(xiàn),系統(tǒng)就知道這個方法由native實現(xiàn)

把java端的native聲明的方法生成對應頭文件.命令行中

 javah  -jni safedemo.jniTest.NativeHelper    NativeHelper是有native方法的java類,用javah聲明

注意.這里有個javah 命令和NativeHelper.java文件的層級關(guān)系問題.我這個命令是在java目錄下執(zhí)行的.

生成navite的頭文件.并把它實現(xiàn).

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>  //這個頭文件是必須的
#ifndef _Included_niTest_NativeHelper
#define _Included_safedemo_jiveHelper
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     safedemo_jniTest_NativeHelper
 * Method:    getAppKey
 * Signature: ()Ljava/lang/String;
 * 這里是對這個jni方法的解釋 classs是java方法所屬的類,然后是方法名,然后是方法的簽名,方法簽名可已唯一確定一個類中的唯一方法.
 */
JNIEXPORT jstring JNICALL Java_safedemo_jniTest_NativeHelper_getAppKey
  (JNIEnv *env, jclass);

#ifdef __cplusplus
}
#endif
#endif

把實現(xiàn)這個接口的文件達成so包.在java中調(diào)用native前引入.就可以執(zhí)行native方法了.這里使用了cmake

public class NativeHelper {
    static {
        System.loadLibrary("native-lib");
    }
        public static native String getAppKey();
}

這樣就建立了so庫中的方法和java中的方法的動態(tài)綁定關(guān)系.

在java中,因為存在多態(tài)性,因此需要通過類名+方法名+方法的簽名(返回值+形參)來唯一確定一個方法.

jni寫的過程稍微有些復雜.但是原理其實是比較簡單的.就是native聲明的java方法和 c代碼進行映射,然后調(diào)用java代碼時執(zhí)行c代碼.

動態(tài)注冊

動態(tài)注冊的主要數(shù)據(jù)結(jié)構(gòu)是JNINativeMethod,他定義在 jni.h里如下

typedef struct {
    const char* name;   //java的方法名
    const char* signature;  //java方法簽名
    void*       fnPtr;  //對應c方法的指針
} JNINativeMethod;

舉個例子

java方法
 public native void jnitest_nativefunction();
native 方法
    JNIEXPORT void JNICALL android_jnitest_1nativefunction(JNIEnv *, jobject);

JNINativeMethod結(jié)構(gòu)
 {"jnitest_nativefunction",       "()V",    (void *)android_jnitest_nativefunction_01}
 ()V 是形參+返回值的組成,java方法沒有形參所以()括號里沒值, 返回值為空,表示為V

然后是要把這種映射結(jié)構(gòu)注冊到系統(tǒng)中

在native代碼被加載時,jint JNI_OnLoad(JavaVM* vm, void* reserved)方法會被調(diào)用.因此我們要實現(xiàn)這個方法,然后注冊java和native方法的映射關(guān)系.如下.這里是標準寫法了.就是把某個java類里的所有映射關(guān)系,通過AndroidRuntime::registerNativeMethods 進行注冊.

這里是java類+java方法 和native方法的指針,一起完成的.

//結(jié)構(gòu)體綁定jnitest_javaclass_a中的兩個接口和本地兩個實現(xiàn)
static JNINativeMethod gMethods_class_a[] = {
        {"jnitest_nativefunction_01",       "()V",    (void*)android_jnitest_nativefunction_01},
        {"jnitest_nativefunction_02",       "()V",    (void*)android_jnitest_nativefunction_02},
};

//此函數(shù)在Java中調(diào)用System.loadLibrary("");時會被自動觸發(fā)推正,在這個函數(shù)中將調(diào)用上面的兩個注冊函數(shù)句占,最終返回JNI版本號
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv *env = NULL;
    jint result = -1;
        //拿到env
    if (vm->GetEnv((void  ) &env, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("ERROR: GetEnv failed\n");
        goto bail;
    }
        //注冊兩個java方法和本地的綁定關(guān)系 com/pioneer/jnitest/jnitest_javaclass是有native方法的類名
    AndroidRuntime::registerNativeMethods(env,
                                          "com/pioneer/jnitest/jnitest_javaclass",
                                          gMethods_class_a, NELEM(gMethods_class_a));
    result = JNI_VERSION_1_4;

    bail:
    return result;
}

jni數(shù)據(jù)類型轉(zhuǎn)換

java中的數(shù)據(jù)類型和native中有基本的對應關(guān)系.特殊的是java的所有對象在native中都是jobjct

基本數(shù)據(jù)類型

image-20200225213307572

引用數(shù)據(jù)類型

所有數(shù)組都編程了 array, 所有對象都是 jobject

jobject                     (all Java objects)
|
|-- jclass                  (java.lang.Class objects)
|-- jstring                 (java.lang.String objects)
|-- jarray                  (array)
|     |--jobjectArray       (object arrays)
|     |--jbooleanArray      (boolean arrays)
|     |--jbyteArray         (byte arrays)
|     |--jcharArray         (char arrays)
|     |--jshortArray        (short arrays)
|     |--jintArray          (int arrays)
|     |--jlongArray         (long arrays)
|     |--jfloatArray        (float arrays)
|     |--jdoubleArray       (double arrays)
|
|--jthrowable

類屬性

屬性變量在 native用 jfieldID 表示 通過Get<>Field ,Set<>Field方法調(diào)用 其中的<>可為各種數(shù)據(jù)類型

        返回值                 方法名                 執(zhí)行環(huán)境    操作對象    操作對象的屬性
        jlong       (*GetLongField)(JNIEnv*, jobject, jfieldID);  
    jfloat      (*GetFloatField)(JNIEnv*, jobject, jfieldID);
    jdouble     (*GetDoubleField)(JNIEnv*, jobject, jfieldID);

    void        (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);
    void        (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean);
    void        (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte);
    void        (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar);

方法 zai native 用 _jmethodID表示 通過 CallStatic<>Method執(zhí)行類的方法, Call<>MethodA執(zhí)行對象的方法


對象方法
        返回值     | 方法名       |   執(zhí)行環(huán)境    |   操作對象    |操作對象的方法 |  方法參數(shù)
        jint         (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
    jint        (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
    jint        (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
    jlong       (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
    jlong       (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
    jlong       (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
    jfloat      (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...);
類方法
        返回值     |   方法名         執(zhí)行環(huán)境|       操作的類    |類的方法   | 方法參數(shù)
    jdouble     (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...);
    jdouble     (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list);
    jdouble     (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
    void        (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
    void        (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list);
    void        (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
最后,方法名最后的A,V或不寫對應執(zhí)行方法的參數(shù) ,A一個參數(shù),V 數(shù)組,不寫表示可變參數(shù)

JNIENV

JNI接口指針僅在當前線程中起作用。這意味著指針不能從一個線程進入另一個線程甚纲。然而库正,可以在不同的咸亨中調(diào)用本地方法

JNIEnv是當前Java線程的執(zhí)行環(huán)境曲楚,一個JVM對應一個JavaVM結(jié)構(gòu),而一個JVM中可能創(chuàng)建多個Java線程褥符,每個線程對應一個JNIEnv結(jié)構(gòu)龙誊,它們保存在線程本地存儲TLS中。因此喷楣,不同的線程的JNIEnv是不同趟大,也不能相互共享使用鹤树。JNIEnv結(jié)構(gòu)也是一個函數(shù)表,在本地代碼中通過JNIEnv的函數(shù)表來操作Java數(shù)據(jù)或者調(diào)用Java方法逊朽。也就是說罕伯,只要在本地代碼中拿到了JNIEnv結(jié)構(gòu),就可以在本地代碼中調(diào)用Java代碼叽讳。

這里都需要傳入一個 JNIEnv,這個是每個線程都有一個代表jni環(huán)境的結(jié)構(gòu)體,這是通過JNIEnv來調(diào)用上邊的各種操作類和對象和方法和屬性的各種方法.這個JNIEnv在每個jni方法的native實現(xiàn)里都是默認提供的.我們需要通過

這里引入一篇很好的文章

http://luori366.github.io/JNI_doc/jni_function_mannual.html

JNIEnv首先指向一個線程相關(guān)的結(jié)構(gòu)追他,該結(jié)構(gòu)又指向一個指針數(shù)組,在這個指針數(shù)組中的每個元素最終指向一個JNI函數(shù)岛蚤。所以可以通過JNIEnv去調(diào)用JNI函數(shù)

java方法簽名

由于java的重載,導致光靠java方法名無法確認一個方法,因此需要方法的形參和返回值(差一點,java中,返回值不同不能代表方法重載,但是java虛擬機是支持返回值不同的重載的)

java的方法簽名格式如

(參數(shù)1類型參數(shù)2類型...)返回值類型,如果類型是對象.要寫L對象全路徑名, 如 String類型寫成L/java/language/String. object對象寫成 L/java/lang/object,一維數(shù)組用[表示

image-20200225220646742

對象引用

因為java有g(shù)c垃圾回收,如果native中持有某個java對象,卻被回收了.就會出問題.因此native端有對java對象的引用封裝,具有不同的回收政策

LocalRef 本地引用.大部分都是這種,當該native函數(shù)返回時,java端就可以回收對應的對象

GlobalRef 全局引用, 需要native端主動釋放該對象,否則java端無法回收,全局引用可以跨方法邑狸、跨線程使用,直到被開發(fā)者顯式釋放

WeakGlobalRef 弱全局引用,運行時可能被回收,所以native端在使用他之前需要判斷是否已經(jīng)被回收

這些也需要通過JNIEnv指針來操作.

 / 
     * 創(chuàng)建 obj 參數(shù)所引用對象的新全局引用, 創(chuàng)建的引用要通過調(diào)用DeleteGlobalRef() 來顯式撤消
     *
     * @param obj 全局或局部引用
     * @return 返回全局引用灭美,如果系統(tǒng)內(nèi)存不足則返回 NULL
     */
    object NewGlobalRef (JNIEnv *env, jobject obj); 
    
    / 
     * 刪除 globalRef 所指向的全局引用
     *
     * @param globalRef 全局引用
     */    
    void DeleteGlobalRef (JNIEnv *env, jobject globalRef); 
    
    / 
     * 創(chuàng)建 obj 參數(shù)所引用對象的局部引用, 創(chuàng)建的引用要通過調(diào)用DeleteLocalRef()來顯式刪除
     *
     * @param obj 全局或局部引用
     * @return 返回局部引用推溃,如果系統(tǒng)內(nèi)存不足則返回 NULL
     */    
    jobject NewLocalRef(JNIEnv *env, jobject ref);
    
    / 
     * 刪除 localRef所指向的局部引用
     *
     * @param localRef局部引用
     */    
    void  DeleteLocalRef (JNIEnv *env, jobject localRef); 
    
    / 
     * 用obj創(chuàng)建新的弱全局引用,
     * @param obj 全局或局部因喲娜
     * @return 返回弱全局引用届腐,弱obj指向null,或者內(nèi)存不足時返回NULL蜂奸,同時拋出異常
     */    
    jweak NewWeakGlobalRef(JNIEnv *env, jobject obj);
    
    / 
     * 刪除弱全局引用
     */    
    void DeleteWeakGlobalRef(JNIEnv *env, jweak obj);

總結(jié)

jni就是安卓和native搭建的一套調(diào)用方式,通過java定義native方法. native端實現(xiàn)方法.兩者盡心綁定.從而實現(xiàn)了java調(diào)用native, 但是這種調(diào)用是在同一線程中實現(xiàn)的.同時也都是從java調(diào)用native方法.native調(diào)用java方法就有點類似反射,也有點像是java虛擬機的執(zhí)行方式.

android studio 中.提供了ndk簡化jni的開發(fā). 他的cmake文件指定把哪些native文件編程一個so包.so包是有針對不同平臺的不同文件.

下邊提供幾個好的博客

http://www.reibang.com/p/87ce6f565d37

http://luori366.github.io/JNI_doc/jni_function_mannual.html

https://www.zybuluo.com/cxm-2016/note/563686

binder 機制

binder載體 parcel

Parcel 是數(shù)據(jù)的容器,用來通過Binder發(fā)送數(shù)據(jù).他的特點是用對象的內(nèi)存直接打包起來,然后發(fā)送到對方的進程.

因此他不適合用于數(shù)據(jù)持久化(如把對象寫到文件中),而只是用于跨進程傳遞.以為他是把對象的完整內(nèi)存都保存并傳遞的.可以理解為夸進程傳遞后.兩個進程里的對象是完全一摸一樣的 .

binder驅(qū)動與協(xié)議

android是基于linux內(nèi)核的.binder驅(qū)動也是一個標準的linux驅(qū)動,android里進程的通過binder的進程間通訊.都是要與binder驅(qū)動打交道的.binder驅(qū)動在內(nèi)核態(tài).binder驅(qū)動的位置是(/dev/binder).

每個響應使用binder通訊的進程.都要與binder驅(qū)動打交道.

binder驅(qū)動提供的接口如下.

binder_poll,(多路復用io機制,簡單說就是同時監(jiān)聽多個io線路的變化,哪個io線路有數(shù)據(jù)變化了.就通知監(jiān)聽者)

各種io資料https://segmentfault.com/a/1190000003063859

binder_open, 打開binder驅(qū)動.每個進程都要,通過(ProcessState打開)每個進程會分配最大4M的binder驅(qū)動內(nèi)存,這個內(nèi)存是逐漸分配的.初始為一頁,同時把這個內(nèi)存和程序的內(nèi)存建立映射關(guān)系.

binder_ioctl, 對驅(qū)動內(nèi)存的操作.主要是讀寫操作.通過這個.將數(shù)據(jù)在進程內(nèi)存和內(nèi)核中傳遞.從而實現(xiàn)ipc

binder_mmap, 內(nèi)存映射.進程和內(nèi)核binder驅(qū)動通過內(nèi)存映射到同一個內(nèi)存空間上,二者都有指向這塊內(nèi)存的指針.這樣內(nèi)核把數(shù)據(jù)拷貝到這個內(nèi)存空間上,對應的進程就拿到了數(shù)據(jù).也就是獲得了跨進程傳遞的數(shù)據(jù).

binder服務管理-ServiceManager

Servicemanager 功能

大部分的系統(tǒng)服務.因為需要使用binder機制,又為了簡化.所以都注冊到ServiceManager中(這一過程也是一次binder通信).而ServiceManager是android在init進程時啟動的.并且作為默認的binder管家.APP可以通過binder號為0而拿到這個ServiceManager的代理.從而獲得其他的系統(tǒng)服務.

ServiceManager在啟動時會把自己注冊為整個系統(tǒng)的binder管家.然后打開binder設(shè)備.進程mmap內(nèi)存映射.

然后ServiceManager就進入了循環(huán),通過ioctl來監(jiān)聽binder驅(qū)動發(fā)來的消息(也就是其他進程與ServiceManager的通信請求),

然后把請求轉(zhuǎn)換到具體的處理函數(shù)中進行處理

最后把處理的結(jié)果寫回給binder驅(qū)動(傳遞回發(fā)送請求的進程.

ServiceManager對外提供的服務如getService,addService,checkServic,listService.

serviceManager會把執(zhí)行服務的結(jié)果寫回binder驅(qū)動

獲取ServiceManager流程

我們?nèi)绾蜗騍erviceManager獲取服務呢?通常有如下幾部

打開binder設(shè)備->執(zhí)行mmap內(nèi)存映射->通過binder驅(qū)動向ServiceManager發(fā)送請求(sm的handle為0)->獲得結(jié)果

這里有些步驟是所有binder進行ipc都需要的.因此需要進行封裝,不用讓我們每次使用時手寫.

ProcessState 是進程唯一的.用來在進程開始時打開binder設(shè)備,執(zhí)行mmap內(nèi)存映射

IPCThreadState 是每個線程唯一的.每個線程都可以進行binder通信,并且biner通信的實際代碼也是在IPCThreadState中實現(xiàn)的.

Proxy. proxy和服務實現(xiàn)一個共同的服務接口I Service,具有相同的功能, proxy在客戶端.客戶端調(diào)用它,就相當于調(diào)用服務進程的特定服務.總層次如下

image-20200301220817905

ServiceManagerProxy

在ServiceManager中.在client會獲取一個代理類. ServiceManagerProxy ,他和服務端的ServiceManager都實現(xiàn)了 IServiceManager 接口,也就是具有相同的功能.而代理類對功能的處理則是通過內(nèi)部的IBinder類的mRemote的 transact 方法.來進行ipc通信.此時這個線程會被掛起.等待服務端返回后才能繼續(xù)執(zhí)行.

BpBinder和BinderProxy

因此我們看到.具體具體業(yè)務的執(zhí)行.都會有proxy代理類轉(zhuǎn)換到IBinder類的對象來處理.那么這個對象那里來的的?

這個Binder就是來自client進程中native端的ProcessState.方法如下,這里創(chuàng)建的就是BpBinder

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)

因為代碼設(shè)計到了java層和native層.所以有很多類在兩層都有相似的名稱.

如binder的客戶端在java層是BinderProxy,在native層是BpBinder.他倆是對應的.并且能夠找到對方.因為java層的binder是由native層的BpBinder對象創(chuàng)建而來的.

這樣. ServiceManagerProxy 的請求方法就轉(zhuǎn)到了client進程中java里的BinderProxy.transact來處理了.而BinderProxy.transact方法是native方法.這又轉(zhuǎn)到了native層.找到對應的native層的BpBinder來處理.

這里簡單說就是client端的 ServiceManagerProxy 的請求,最后倒流client端native里的BpBinder.transact方法里了.

而BpBinder.transact的方法.又是由 IPCThreadState . transact 來執(zhí)行的.

流程如下,下邊所有方法都是client進程的.

(Java)ServiceManagerProxy.getService ->(java)BinderProxy.transact ->(native)BpBinder.transact->(native)IPCThreadState.transact

ProcessState

一個進程只有一個ProcessState.他在創(chuàng)建時打開binder驅(qū)動,進行mmap內(nèi)存映射.也就是一個進程之后進行一次內(nèi)存映射,里邊的線程都使用者一塊內(nèi)存.并且ProcessState會保存該進程創(chuàng)建的所有的binder信息,這里的binder是BpBinder. 每個BpBinder都和一個int的handle綁定.用來指明這個代理的binder的主人.

主要.這時候.新建的BpBinder只有一個handle標識.并沒有和遠程服務有什么關(guān)系.ServiceManager作為系統(tǒng)服務的大管家,他的handler默認是0.

上文我們看到.BpBinder.transact最后轉(zhuǎn)化到了IPCThreadState.transact.的方法里.這里的IPCThreadState是線程單實例的.我們繼續(xù)分析IPCThreadState,他是真正通過binder驅(qū)動與遠方發(fā)送數(shù)據(jù)的地方.

IPCThreadState進行ipc通信

transact函數(shù)

Handle 表示要照的server的服務的標記, code是要執(zhí)行什么服務,data和reply是發(fā)送的數(shù)據(jù)和接受的響應.flags是一些模式.可以指定異步,異步就是直接返回.不等待執(zhí)行結(jié)果.
status_t IPCThreadState::transact(int32_t handle,uint32_t code, const Parcel& data,
  Parcel* reply, uint32_t flags)
{
 flags |= TF_ACCEPT_FDS;
    if (err == NO_ERROR) { 把要發(fā)出的數(shù)據(jù)整理好.寫到mOut里.
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }
    
    if ((flags & TF_ONE_WAY) == 0) {發(fā)送數(shù)據(jù),得到結(jié)果
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        
    }
    return err;
}

waitForResponse函數(shù)

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;

    while (1) {
            //真正執(zhí)行與驅(qū)動交換數(shù)據(jù)
        if ((err=talkWithDriver()) < NO_ERROR) break;
        cmd = mIn.readInt32(); //返回結(jié)過的不同處理.一會在看
        switch (cmd) {
            case BR_TRANSACTION_COMPLETE:
          case BR_FAILED_REPLY:
            case BR_REPLY:
        }
    }

talkWithDriver

這個函數(shù)讀寫都是在這執(zhí)行.具體是讀還是寫則是有mIn和mOut的數(shù)據(jù)決定的.,把數(shù)據(jù)寫入bwr中.

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    //這個是與驅(qū)動打交道的數(shù)據(jù)結(jié)構(gòu).他負責寫出數(shù)據(jù)和讀入數(shù)據(jù)
    binder_write_read bwr;
    
    //mOut就是負責要寫出的書,min是負責讀入的數(shù)據(jù),他們里邊都有個位置計算已經(jīng)讀了多少,還剩多少.
    //需要用mIn和mOut來初始化bwr, 來確定需要讀寫的數(shù)據(jù)和數(shù)據(jù)大小
    bwr.write_size = outAvail;
    bwr.write_buffer = (long unsigned int)mOut.data();

    // 這里是處理要讀的數(shù)據(jù)
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (long unsigned int)mIn.data();
    } else {
        bwr.read_size = 0;
    }
    status_t err;
    do {
         //與binder驅(qū)動進行數(shù)據(jù)交換. BINDER_WRITE_READ這個命令很重要.我們以后要驅(qū)動的里這部分.
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
    } while (err == -EINTR);
    //根據(jù)數(shù)據(jù)交換的結(jié)果.來確定本次讀了多少.寫了多少.更新mIn和mOut的數(shù)據(jù)尺度.
    if (err >= NO_ERROR) {
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < (ssize_t)mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else    
                mOut.setDataSize(0);
        }
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
         
        return NO_ERROR;
    }
    return err;
}

mIn和mOut解釋如下,他倆都是Parcel類的對象.MDataCapacity表示改對象的數(shù)據(jù)最大容量.

其實讀寫的過程就是要使mDataPos最后等于mDataSize.

image-20200301234611274

上邊通過ioctl和驅(qū)動進行了數(shù)據(jù)交換,再把交換結(jié)果通過mIn和mOut更新.也就知道本次寫和讀的情況.

ioctl(binder驅(qū)動)

如果是同步執(zhí)行(就是等待返回結(jié)果).會把client進程掛起.等待服務端響應后在喚醒服務端.

copy_from_user從用戶空間負責數(shù)據(jù)到內(nèi)核中.

根據(jù)用戶進程中talkWithDriver中填寫的bwr數(shù)據(jù)分別處理.

有數(shù)據(jù)要寫出就執(zhí)行binder_Thread_write,有數(shù)據(jù)要讀入就執(zhí)行binder_thread_read.如果失敗了.還有把負責到內(nèi)核的數(shù)據(jù)在復制回用戶控件.

image-20200302001403716
image-20200302001425557

下邊看binder_Thread_write如何寫出數(shù)據(jù)(這里有個疑問.已經(jīng)到了驅(qū)動,但是沒看到那個標識指向要訪問的服務端啊?--更正,在IpcThreadState.writeTransactionData包裝數(shù)據(jù)時候,把handle寫在了mOut里了.)

通過客戶端指定的handle.找到目標對象的target_node.進入找到目前對象的進程target_proc和線程target_Thread.

拿到target_thread目標線程的todo和wait列表.然后生成一個binder_transaction變量,加入到目標線程的todo隊列中.(這樣目標線程被喚醒,就可以從list中拿到client端的請求).又生成一個binder_work變量,加入到client對應線程的todo隊列中.

然后填寫發(fā)給對方的binder_transaction數(shù)據(jù),包括調(diào)用者和目標對象的信息,和本次轉(zhuǎn)換的內(nèi)存.

最后喚醒對方服務進程.

這是分成兩路.client的執(zhí)行和server的執(zhí)行.

驅(qū)動返回client

先看client執(zhí)行binder_Thread_write后又判斷是否要讀,會執(zhí)行binder_Thread_read

從client的todo隊列中取出一個請求(上邊寫數(shù)據(jù)的時候放進來的binder_work),取出的這個命令又向上報.直接到IPCThreadState.waitForResponse中talkWithDriver后的命令處理,此時命令cmd為BR_TRANSACTION_COMPLETE,然后結(jié)束此次循環(huán),再次開始執(zhí)行while循環(huán),繼續(xù)talkWithDriver.

這就代表是本次發(fā)送已經(jīng)完成.然后繼續(xù)talkWithDriver與驅(qū)動交互.但這次已經(jīng)沒有數(shù)據(jù)要寫出了.因此只執(zhí)行數(shù)據(jù)讀.然后進行睡眠.等待服務端處理完在喚醒.

也就是說.在client端.talkWithDriver把數(shù)據(jù)寫出后,內(nèi)核返回信息使client再次執(zhí)行waitForResources里的wihle循環(huán),然后再等待讀取消息時進入睡眠.

大概流程倒是這樣的

binder-clent12415

serviceManager被喚醒

ServiceManager在啟動時,會先打開binder驅(qū)動,然后進行mmap.再把自己設(shè)置成系統(tǒng)的大管家.然后就進入了一個無線的for循環(huán),通過ioctl監(jiān)聽只讀請求,等待客戶端請求.因此會進入驅(qū)動中的binder_Thread_read.此時沒有數(shù)據(jù)到來.就會進入休眠

ServiceManager被喚醒后.會檢查todo隊列是否有要處理的請求.如果有.就交給ServiceManager來處理.此時會到ServiceManager的 binder_parse 中解析

binder_parse中,對不同命令進行分別處理.主要作用就是得到客戶端要請求的數(shù)據(jù),進行封裝.然后通過ioctl交給binder驅(qū)動,寫回給用戶端.

ServiceManager中,組裝的數(shù)據(jù)是各個注冊到ServiceManager中的服務對應的一個binder驅(qū)動中的id號.通過這個id.可以找到對應的服務.(驅(qū)動中,通過這個id號,可以找到對應進程中對binder驅(qū)動進行的內(nèi)存映射區(qū)域,然后就能給這個進程發(fā)送數(shù)據(jù)了)

ServiceManager中組裝好數(shù)據(jù)后,里邊包含要獲得的服務的指針,在通過ioctl寫回驅(qū)動.

驅(qū)動里從這數(shù)據(jù)中拿到要返回的client的信息,找到client的進程和線程,找到對應的todo和wait隊列,給client的todo隊列寫一個變量.給ServiceManager的todo隊列也寫一個變量.然后喚醒client進程.并把ServiceManager要發(fā)送給client的數(shù)據(jù)返回給他.

ServiceManager在接著處理我方todo隊列的消息.然后也隨之進入睡眠了.等待下一次喚醒

內(nèi)核在通過copy_to_user,把數(shù)據(jù)復制到client進程的空間.用戶控件就繼續(xù)執(zhí)行talkWithDriver之后的代碼.此時就拿到了server端的數(shù)據(jù)了.

client線程在把收到的數(shù)據(jù)轉(zhuǎn)換成一個binder.在提供出去,在封裝成各種 proxy.就可以使用了.

binder-server12415

在驅(qū)動中,各個進程的binder是通過一個句柄值(handle)來區(qū)分的.服務端也是把這個句柄值和一些數(shù)據(jù)返回給用戶端,從而完成夸進程通信.

總體概括

binder 是安卓特有的進程間通訊的機制.因為不同進程的內(nèi)存是不同的.不能直接進行數(shù)據(jù)交換.linux本有一些跨進程通訊的方式.本質(zhì)上都是把數(shù)據(jù)在兩個進程間傳遞.而binder機制是為安卓設(shè)計的.他的特點是只涉及一次內(nèi)存復制.就比較高效.且binder機制進行了分層.把底層的數(shù)據(jù)拷貝和上層也業(yè)務進行了分類.使上層使用時,不必寫重復的bindner先關(guān)的代碼.進程都有一個binder標準.通過該標志,可以找到對應的進程.

考慮一下傳統(tǒng)的IPC方式中犁苏,數(shù)據(jù)是怎樣從發(fā)送端到達接收端的呢?通常的做法是扩所,發(fā)送方將準備好的數(shù)據(jù)存放在緩存區(qū)中围详,調(diào)用API通過系統(tǒng)調(diào)用進入內(nèi)核中。內(nèi)核服務程序在內(nèi)核空間分配內(nèi)存祖屏,將數(shù)據(jù)從發(fā)送方緩存區(qū)復制到內(nèi)核緩存區(qū)中助赞。接收方讀數(shù)據(jù)時也要提供一塊緩存區(qū),內(nèi)核將數(shù)據(jù)從內(nèi)核緩存區(qū)拷貝到接收方提供的緩存區(qū)中并喚醒接收線程袁勺,完成一次數(shù)據(jù)發(fā)送雹食。這種存儲-轉(zhuǎn)發(fā)機制有兩個缺陷:首先是效率低下,需要做兩次拷貝:用戶空間->內(nèi)核空間->用戶空間期丰。Linux使用copy_from_user()和copy_to_user()實現(xiàn)這兩個跨空間拷貝群叶,在此過程中如果使用了高端內(nèi)存(high memory),這種拷貝需要臨時建立/取消頁面映射钝荡,造成性能損失街立。其次是接收數(shù)據(jù)的緩存要由接收方提供,可接收方不知道到底要多大的緩存才夠用埠通,只能開辟盡量大的空間或先調(diào)用API接收消息頭獲得消息體大小赎离,再開辟適當?shù)目臻g接收消息體。兩種做法都有不足端辱,不是浪費空間就是浪費時間

image-20200301203239363

binder機制最底層是binder驅(qū)動.他是如何實現(xiàn)一次復制就在兩個進程中傳送數(shù)據(jù)的?

原理如下圖

image-20200301203437457

驅(qū)動層

這利用了內(nèi)存映射的原理,我們假設(shè)目前是A進行向B進程發(fā)送數(shù)據(jù).而在B進程啟動之初,就對binder驅(qū)動進行了內(nèi)存映射,(binder驅(qū)動工作在內(nèi)核態(tài),可以訪問任意的內(nèi)存地址),操作是先打開binder驅(qū)動.在操作獲得的描述符進行內(nèi)存映射,內(nèi)存映射的結(jié)果就是binder驅(qū)動和B進程都獲得了一塊物理內(nèi)存,并且binder驅(qū)動和B進程都有一個指針指向這個物理內(nèi)存(這個物理內(nèi)存在內(nèi)核上分配),這樣.b進程和Binder驅(qū)動任意一方對這個內(nèi)存的操作,都能讓對方知道變化.當A進程要向B發(fā)送數(shù)據(jù)時,會調(diào)用Binder驅(qū)動,binder驅(qū)動通過copy_from_user把數(shù)據(jù)從進程A中拷貝到Binder驅(qū)動中,而拷貝的內(nèi)存位置就是進程B和Binder驅(qū)動進行映射的這塊內(nèi)存.這樣從進程A來說,就是把數(shù)據(jù)從用戶進程A拷貝到了binder驅(qū)動的內(nèi)核中.而對于進程B.則看到驅(qū)動把數(shù)據(jù)從進程A直接拷貝到自己的內(nèi)存中.這就實現(xiàn)了一次拷貝在兩個進程中傳遞數(shù)據(jù).

這里幾個注意的點. 進程與binder驅(qū)動建立內(nèi)存映射的時候.使用的內(nèi)存是binder驅(qū)動的,是在內(nèi)核中.

進程B建立內(nèi)存映射后.通過監(jiān)聽這端內(nèi)存的io變化.就知道是否有數(shù)據(jù)到來了.

因此.每個進程在啟動的時候,都會與binder驅(qū)動建立內(nèi)存映射關(guān)系,(在開始時,只分配一個比較小的內(nèi)存用來提高進程創(chuàng)建速度,且節(jié)省內(nèi)存)

Binder層

在驅(qū)動層面已經(jīng)實現(xiàn)了基礎(chǔ)的數(shù)據(jù)傳輸.這過程是固定且麻煩的.因此系統(tǒng)進程封裝后.我們直接使用就可以.

在業(yè)務層.要處理的就是.把數(shù)據(jù)封裝成固定的格式.交給驅(qū)動去傳遞.然后到對方進程時能正確的解析出來.進行處理.

這里使用了parcel,他是一種數(shù)容器.直接把要傳輸?shù)臄?shù)據(jù)的整個內(nèi)存給傳輸過去了.保證兩邊對象的一致性.

為了進一步簡化,系統(tǒng)有封裝了binder這一結(jié)構(gòu). 他代表在兩邊傳遞的數(shù)據(jù).android里大多用binder的是各種服務.

因此先定義一種共同的接口interface,在兩個進程實現(xiàn).client中則是把請求轉(zhuǎn)換成binder處理.而server端就是從binder中拿出請求,完成具體的工作.這里就是更進一步的抽象封裝,業(yè)務層只需要設(shè)計具體的服務如何提供,然后再把數(shù)據(jù)一封裝就完成了

這里產(chǎn)生的binder層,在client端是bpbinder,在server端是bbinder.這是用來封裝數(shù)據(jù)并發(fā)送過去的一層.

代理層

在上邊是業(yè)務層.兩端都有實現(xiàn)同樣接口的類.處理具體的功能,如activitymanagerservice在服務端,對應客戶端是activitymanagerproxy(我自己起的名字,) 客戶端的調(diào)用 activitymanagerproxy的具體功能,activitymanagerproxy再把數(shù)據(jù)交個bpbinder進行封裝.bpbinder封裝完成后,把數(shù)據(jù)通過IpcThreadState來發(fā)送數(shù)據(jù),在通過binder驅(qū)動發(fā)送過去.在server端.先是binder驅(qū)動的監(jiān)聽知道數(shù)據(jù)到來.在轉(zhuǎn)成bbinder解析數(shù)據(jù),在交給activitymanagerservice執(zhí)行.

同時為了簡化.加入了ServiceManager,統(tǒng)一管理所有的系統(tǒng)服務.系統(tǒng)服務要先注冊到ServiceManager,才算對外暴露.APP像使用什么服務時,先想ServiceManager,得到一個對應服務的代理.這個代理把想執(zhí)行的請求,和遠端的binder是具有聯(lián)系的.通過他就可以進行binder通信

上文的binder.不一定是framework中的binder類.只是一個功能的描述.

所以.最底層是binder驅(qū)動.需要每個進程在啟動時打開驅(qū)動,進行內(nèi)存映射,通過ProcessState

上層是IPCThreadState.他是每個線程唯一,真正用數(shù)據(jù)與binder驅(qū)動交互的地方.發(fā)送數(shù)據(jù),得到請求

在上層是Binder,在發(fā)送數(shù)據(jù)時,通過binder把數(shù)據(jù)發(fā)給IPCThreadState,而在獲得了遠程的數(shù)據(jù)返回后.IPCThreadState又回把數(shù)組封裝成一個binder,向上傳遞.

可以這樣理解,每個線程可以進行很多次 跨進程通訊,但是每次可能遠程通信都不同. IPCThreadState提供了遠程通信的通道.但是通道的內(nèi)容是由binder決定里的.所以同一個線程里.通道有一個,但是內(nèi)容有很多.

在上一層.把binder封裝成proxy.提供給客戶端.就徹底隱藏了底層的復雜機制.上層只需要操作這個代理類就可以了.

最外邊則是通過ServiceManager的代理類來拿到其他服務的代理. ServiceManager作為系統(tǒng)服務關(guān)鍵.默認為handle號為0.可以直接獲取他的代理.這次,在看這個分層.就很清楚了.

同時還要知道一點.java層的binder只不過是native層的一個鏡像.二者是綁定的.java層主要功能都是通過native層來實現(xiàn)的

image-20200301220817905

系統(tǒng)啟動

系統(tǒng)組成

系統(tǒng)啟動時,會加載一系列的其他服務,這是通過讀取配置文件實現(xiàn)的.

系統(tǒng)啟動的第一個進程是init進程,他會讀取init.rc配置文件來啟動過各種服務

init.rc

分為 actio(動作),commands(命令) services(服務)options(選項)

on<trigger> 觸發(fā)條件                                                                            
    <command1> 執(zhí)行命令1                                                                        
    <command2>  執(zhí)行命令2                                                                       
    也就是在發(fā)送了trigger動作時,執(zhí)行命令1和命令2 
舉例
on boot //觸發(fā)boot事件時
mkdir  /dev  創(chuàng)建dev目錄
class_start default 啟動所有default標
    
service 是定義的可執(zhí)行程序,他會列出要執(zhí)行的文件的路徑,和附帶的約束,通常運行于另外的進程中.
services <name><pathname>[ arguement]
    <option>
    <option>
舉例
service zygote /system/bin.app_process  定制zygote服務
    socket zygote 666                                           啟動socket,

ServiceManager(init啟動)

ServiceManager也是由init.rc配置文件里面并由 init進程 啟動的一個額外的進程.他同zyote進程綁定.同生共死.他負責所有binder通信的注冊和管理.

zygote(init啟動)

也是由init.rc進程創(chuàng)建的另一個進程.用于fork進程用,他啟動后,會通過socket 監(jiān)聽systemServer發(fā)來的請求.然后fork自己創(chuàng)建新進程.安卓里大多數(shù)應用進程和系統(tǒng)進程都是他fork出來的.也是由init.rc配置文件里面試并由init進程啟動的.他死亡時會殺掉他所有創(chuàng)建的子進程.

主要流程

調(diào)用AppRuntime.start.把任務都交個他處理

AppRuntime.start里.創(chuàng)建java虛擬機,動態(tài)注冊大量jni函數(shù),調(diào)用ZygoteInit.main方法.

ZygoteInit.main里 1注冊ZygoteInit用的socket.自己作為服務端,2反射加載類和framework.apk中資源.3通過forkSystemServer來啟動system_server進程4.開啟循環(huán).等待socket客戶端的連接.

zygote進程和systemServer進程是同生共死的.systemServer死了.zygote進程也會殺死自己.

systemServer(zygote啟動)

由zygote進程創(chuàng)建的另一個進程.由zygote啟動的虛擬機中的java類ZygoteInit來啟動.通過forkSystemServer.來生產(chǎn)一個新進程.用于加載各種服務.

啟動時執(zhí)行 handleSystemServerProcess 里邊調(diào)用了RuntimeInit. zygoteInit

這里邊創(chuàng)建了1.ProcessState(進程獨有的)與binder通信系統(tǒng)建立聯(lián)系.然后2.啟動SystemServer(java)的main函數(shù)

SystemServer里執(zhí)行了init1,開啟圖形和聲音的服務和 init2,啟動了serverThread,初始化各種服務.又執(zhí)行ProcessState和IpcThreadState的線程服務,開啟了兩個binder通信線程.

總之就是在native端和java端啟動了各種服務.init1的服務是native的.init2的服務是java端的,通過線程啟動.

啟動其他進程

當需要啟動一個進程時.通過向zygote進程發(fā)送socket信息來讓zygote進程fork出新進程.以activitymanager.創(chuàng)建socket及發(fā)送給zygote是有process.java類實現(xiàn)

zygoteInit.java中通過ZygoteConnection.runOnce來處理收到的請求.然后就是fork出新進程.然后讓子進程處理請求.

image-20200304001205885

ActivityManagerService

AMS由SystemServer創(chuàng)建,并且SystemServer會等AMS啟動完在繼續(xù)執(zhí)行.

原理是SystemServer調(diào)用AMS的main方法. main方法中開啟一個線程.然后SystemServer會在這里wait,等到線程里創(chuàng)建AMS成功后,AMS的線程notify喚醒SystemServer繼續(xù)工作.

Ams的主要任務是負責系統(tǒng)內(nèi)四大組件的啟動,切換,調(diào)用及應用進程的管理和調(diào)度.

ActivityStack 則是用來管理Activity狀態(tài)的類.他里邊保存了幾個集合,用來存儲各種狀態(tài)的Activity

image-20200304221404144

Activity在Ams里都抽象成了ActivityRecord 類

值得注意的是,AMS因為需要調(diào)用用戶進程,所以每個進程在啟動時候,都會與ams建立鏈接,同時ams也會獲得一個向用戶進程發(fā)消息的binder(ApplicationThread),通過他,AMS就可以主動向用戶進程發(fā)消息,執(zhí)行一些操作.此時AMS變成了binder的client端,用戶進程成了binder的server端. 這種方式是匿名binder.

AMS啟動

Ams在SystemServer的線程中啟動,大概步驟如下,也就是先啟動Ams.然后又等待Ams的啟動回調(diào).

//創(chuàng)建線程,啟動

context = ActivityManagerService. main (factoryTest);

//把systemServer 加入到Ams中管理

ActivityManagerService. setSystemProcess ();

//初始化系統(tǒng)provider

ActivityManagerService. installSystemProviders ();

//Ams和Wms(WindowManagerService)建立聯(lián)系

((ActivityManagerService)ServiceManager. getService ("activity"))

? . setWindowManager (wm);

//ams啟動完成的回調(diào),ams的 systemReady 完成后,其他服務的 systemReady 才能被調(diào)用.

((ActivityManagerService)ActivityManagerNative. getDefault ())

? . systemReady (new Runnable () {})

ActivityManagerService.main

啟動一個線程AThread,創(chuàng)建AMS后進入looper循環(huán)

Ams的創(chuàng)建中初始化幾個系統(tǒng)運行狀態(tài)服務,如ProcessStats,是一個統(tǒng)計信息的服務.并創(chuàng)建一個線程用來更新統(tǒng)計信息.

創(chuàng)建幾個系統(tǒng)服務service

通過ActivityThread.main 創(chuàng)建ActivityThread(此時在SystemServer線程).

ActivityThread會為"android"(也就是faramwork.apk)這個app創(chuàng)建application和context并進行關(guān)聯(lián).這是系統(tǒng)資源APP.所以AMS為他初始化了和應用APP一樣的環(huán)境.

總結(jié)就是.啟動一個loop線程,創(chuàng)建AMS,為系統(tǒng)APP創(chuàng)建ActivityThread.Application,Context.

此時的ActivityThrad運行在SystemServer進程中.是系統(tǒng)APP(framework.apk)的主線程

ActivityManagerService.setSystemProcess

把AMS注冊到ServiceManager中.

獲取系統(tǒng)APP(framework.apk)的ApplicationInfo,ApplicationInfo是PackageManagerServic中對應用信息的抽象.對應AndroidManifest里的application節(jié)點

調(diào)用ActivityThread. installSystemApplicationInfo 這里真正將系統(tǒng)APP的ApplicationInfo和Context綁定起來.此時 AMS為系統(tǒng)APP在System_server中制造的APP的運行時環(huán)境才算完整.

為系統(tǒng)APP創(chuàng)造ProcessRecord,代表進程的記錄.并保存在AMS中.ProcessRecord是AMS里對APP進程的一個抽象.用來保存進程的信息. ProcessRecord中還保存了AMS和用戶進程通信的IApplicationThread,在客戶端對應的則是ApplicationThread. AMS通過IApplicationThread來調(diào)用客戶端,這也是一個binder通信(通常都是binder通信.但是在系統(tǒng)服務這里.是同一進程的.則不是binder通信)

總結(jié),主要是把AMS注冊到ServiceManager,然后通過PKMS返回的ApplicationInfo,創(chuàng)建system_server進程的ProcessRecord.加入AMS的管理.(因為framework.apk.運行在system_server進程)

ActivityManagerService.systemReady

發(fā)送ACTION_PRE_BOOT_COMPLETED廣播

殺死先于AMS啟動的應用進程.

完成其他服務的 systemReady 方法,再啟動系統(tǒng)桌面home

home啟動成功后,會回到Ams的finishBooting方法,然后Ams發(fā)送ACTION_BOOT_COMPLETED廣播.

總結(jié)

AMS的main函數(shù),創(chuàng)建AMS對象,開啟一個looper線程,為framework.apk 初始化環(huán)境(創(chuàng)建ActivityThread.Application,Context.)

AMS的setSystemProcess . 注冊AMS和一些運行信息服務到ServiceManager.并為system_server創(chuàng)建processRecord.表示system_server進程也作為AMS管理的用戶進程.(因為system_server中有framework.apk等)

AMS的installSystemProviders. 為AMS加載 SettingsProvider

AMS的systemReady.掃尾工作.啟動系統(tǒng)桌面. Home Activity.

startActivity 分析

從Am.java 分析Activity的啟動. am是 提供的腳本.用來和AMS交互. 如

am start -W -n com.test/MainActivity 啟動com.test包下的 MainActivity. am 命令要 通過adb shell 登錄手機后執(zhí)行

ActivityStack簡介

mMainStack是ActivityStack類. 他負責組織 Activity的task棧. task棧是用來完成同一任務的結(jié)果.不同APP的Activity可以在一個棧里. 安卓還支持多個task 棧.但是只能有一個在前臺, 當一個棧在前臺時,點擊返回按鈕,則會把這個task棧的所有Activity都清空,然后在回調(diào)別的task棧上.

ActivityStack 及相關(guān)成員如下

image-20200307130000093

可以看到.在AMS中.所有的Activity都抽象成ActivityRecord. 而task則由TaskRecord表示. ActivityRecord知道自己屬于哪個task.而ActivityStack則通過mHistory管理ActivityRecord.

image-20200307130710483

ActivityStack里有很多不同狀態(tài)的ActivRecord ,因此ActivityStack肯定要讓ActivityRecord在不同狀態(tài)切換

ArrayList<ActivityRecord> mStoppingActivities

ArrayList<ActivityRecord> mGoingToSleepActivities

ArrayList<ActivityRecord> mWaitingVisibleActivities

ArrayList<ActivityRecord> mHistory

ActivityStack 主要功能如下

topRunningActivityLocked 找到正在運行的ActivityRecord

findActivityLocked 根據(jù)intent和ActivityInfo.來匹配ActivityRecord

所有大概來看 ActivityStack 就是通過保管TaskRecord和ActivityRecord. 來管理Activity和task 的關(guān)系.(四種啟動模式)

ActivityStack.startActivityMayWait

AMS的啟動從 startActivityAndWait 開始

mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
                resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
                res, null, options, userId);

startActivityMayWait 的大概流程是.

創(chuàng)建ActivityRecord.得到TaskRecord(如果有FLAG_ACTIVITY_NEW_TASK標識),還要啟動一個新進程來加載Activity實例.如果當前有展示的Activity.還要停止當前Activity.精簡代碼如下

final int startActivityMayWait(){
//1.通過intent 從 PackageManagerService中得到ActivityInfo
ActivityInfo aInfo = resolveActivity(intent, resolvedType, debug,
                profileFile, profileFd, autoStopProfiler);
      //2.執(zhí)行函數(shù)Activity啟動          
     int res = startActivityLocked(caller, intent, resolvedType,
                    grantedUriPermissions, grantedMode, aInfo,
                    resultTo, resultWho, requestCode, callingPid, callingUid,
                    onlyIfNeeded, componentSpecified, null); 
 //3.等待用戶進程啟動成功后回調(diào)AMS,保存該Activityinfo 啟動結(jié)果.

 if (res == IActivityManager.START_SUCCESS) {
         mWaitingActivityLaunched.add(outResult)
         do {
                 try {
                     mService.wait();
                   } catch (InterruptedException e) {
                  }
             } while (!outResult.timeout && outResult.who == null);
       }
                    
}

那么.第二步就是啟動Activity的地方.這里有兩個同名函數(shù),此時是走參數(shù)多的那個.

ActivityStack.startActivityLocked(參數(shù)多的)

主要是進行權(quán)限的檢查,和activity的來龍去脈的設(shè)置

  1. 先是拿到調(diào)用者進程的pid和uid.看是否正確,
  2. 在設(shè)定 要啟動Activity的 sourceRecord和resultRecord這倆都是(ActivityRecord),表示誰啟動的新Activity.和新Activity完成后 返回到那里.
  3. 處理intent的flag
  4. 檢查調(diào)用者權(quán)限.用1步驟的pid和uid看調(diào)用者使用有權(quán)限啟動Activity.
  5. 創(chuàng)建要啟動Activity的ActivityRecord對象.
  6. 啟動Activity.
startActivityLocked{
//1.先是拿到調(diào)用者進程的pid和uid.看是否正確
  if (callerApp != null) {
      callingPid = callerApp.pid;
      callingUid = callerApp.info.uid;
  }

  2.//然后在設(shè)定 要啟動Activity的 sourceRecord和resultRecord這倆都是(ActivityRecord),表示誰啟動的新Activity.和新Activity完成后 返回到那里
   ActivityRecord sourceRecord = null;
  ActivityRecord resultRecord = null;

  3.處理flag
    int launchFlags = intent.getFlags();
  4. 檢查調(diào)用者權(quán)限
      final int perm = mService.checkComponentPermission(aInfo.permission, callingPid,
                callingUid, aInfo.applicationInfo.uid, aInfo.exported);
  5. 創(chuàng)建一個ActivityRecord 對象.就是我們要啟動的Activity的在AMS的代表
      ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
                intent, resolvedType, aInfo, mService.mConfiguration,
                resultRecord, resultWho, requestCode, componentSpecified);
    6.啟動Activity
    err = startActivityUncheckedLocked(r, sourceRecord,
                grantedUriPermissions, grantedMode, onlyIfNeeded, true);
}


ActivityStack.startActivityUncheckedLocked

  1. 根據(jù)intent.flag來為新的Activity選擇合適的task.
  2. 找到一個ActivityRecord
  3. 為新的ActivityRecord 設(shè)置 taskRecord,此時新Activity的 ActivityRecord 和TaskRecord都準備好了
//1.根據(jù)intent.flag來為新的Activity選擇合適的task.
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
//2. 找到合適的 ActivityRecord
  ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
                        ? findTaskLocked(intent, r.info)
                        : findActivityLocked(intent, r.info);
//3.為 新的ActivityRecord 設(shè)置 taskRecord
if (reuseTask == null) {
      r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
  } else {
      r.setTask(reuseTask, reuseTask, true);
  }
  //一個同名函數(shù),這次是參數(shù)少的,里邊主要是執(zhí)行 resumeTopActivityLocked.
  startActivityLocked(r, newTask, doResume, keepCurTransition, options);

ActivityStack.resumeTopActivityLocked

  1. 找到要啟動的Activity
  2. 暫停舊的Activity
  3. 新的Activity和wms搭配,繼續(xù)執(zhí)行新Activity的啟動.
//找到隊列中棧頂中運行的Activity.這是我們要啟動的那個
ActivityRecord next = topRunningActivityLocked(null);
//暫停目前正在顯示的Activity.(暫停原來的Activity.好讓新的Activity顯示出來)
//這里.ams會等待舊Activity暫停.暫停后客戶端會回調(diào)ams的resumeTopActivitLocked.然后繼續(xù)啟動新的activity
if (mResumedActivity != null) {
      (userLeaving, false);
      return true;
  }
//  再次執(zhí)行時,舊Activity已經(jīng)暫停了.此時要啟動新Activity.
  //新Activity和wms綁定
   if (SHOW_APP_STARTING_PREVIEW && mMainStack) {
          mService.mWindowManager.setAppStartingWindow(
                  next.appToken, next.packageName, next.theme,
                  mService.compatibilityInfoForPackageLocked(
                          next.info.applicationInfo),
                  next.nonLocalizedLabel,
                  next.labelRes, next.icon, next.windowFlags,
                  null, true);
      }
  }
  //繼續(xù)啟動新Activity.
  startSpecificActivityLocked(next, true, true);

此時這里產(chǎn)生了分叉.舊的Activity需要暫停.然后新的Activity在進行啟動.我們先看新的Activity的啟動.舊Activity的暫停以后再看

ActivityStack.startSpecificActivityLocked

  1. 先拿到新Activity對應的進程
  2. 如果進程已經(jīng)啟動.就繼續(xù)啟動Activity
  3. 進程沒啟動,先啟動Activity所在進程
processRecord 是進程在AMs的抽象對象. 這里看新Activity的進程是否存在
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid);
  //如果集成已經(jīng)啟動.就直接啟動Activity.
  if (app != null && app.thread != null) {
                app.addPackage(r.info.packageName);
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
  }
  //進程沒啟動,先啟動進程
  mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false);

這里又分叉了. 我們按復雜的走.先啟動進程

AMS.startProcessLocked

  1. 通過啟動ActivityThread 來啟動用戶進程,同時保存用戶進程的ProcessRecord在ams中

ActivityThread 就是用戶線程.當然這個本質(zhì)是通過zygote進程fork出的新進程.然后執(zhí)行ActivityThread的main函數(shù).

  1. 發(fā)送延時消息(10s),如果10s內(nèi)用戶進程沒啟動完畢并向AMS注冊.則認為用戶進程啟動失敗.
來啟動用戶進程,執(zhí)行ActivityThread ,同時保存用戶進程的ProcessRecord在ams中
 Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
                    app.processName, uid, uid, gids, debugFlags,
                    app.info.targetSdkVersion, null);
  AMS發(fā)送一個超時消息.等待Activity的進程啟動完與ams溝通.也就是如果這個超時消息響應時,新Activity的進程還沒啟動完并與ams溝通.則認為 用戶進程啟動失敗.
  synchronized (mPidsSelfLocked) {
      this.mPidsSelfLocked.put(startResult.pid, app);
      Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
      msg.obj = app;
      mHandler.sendMessageDelayed(msg, startResult.usingWrapper
              ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
  }
image-20200307164943836

到這里.我們知道.一個Activity 的啟動.在AMS端.需要有一個ActivitRecord,TaskRecord.ProcessRecord.來對應.

同時,還要處理他原有Activity的暫停,舊的Activity的暫停.早于新Activity 的啟動.

ActivityThread.main

  1. 開啟消息循環(huán)loop
  2. 創(chuàng)建ActivityThread對象.執(zhí)行attach方法

        Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }
    
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
    
        Looper.loop();

ActivityThread.attach

  1. 主要就是與AMS建立binder通信. 這里的mAppThread 是ApplicationThread類,這里ActivityThread的內(nèi)部類,傳遞給AMS,AMS就可以通過 他來主動調(diào)用ActivityThread的方法.也就是AMS主動通過Binder與ActivityThread交流.此時AMS是binder的client端.
 IActivityManager mgr = ActivityManagerNative.getDefault();
        try {
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
        }

ApplicationThread 在收到AMS的消息后.會通過handler.把消息發(fā)給ActivityThread來執(zhí)行.

   public final void scheduleResumeActivity(IBinder token, boolean isForward) {
            queueOrSendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0);
        }

ActivityThread 里的attac方法.接收方是AMS里的 attachApplicationLocked

AMS. attachApplicationLocked

  1. 把用戶進程發(fā)來的binder和AMS對應的ProcessRecord綁定起來.
  2. 移除AMS之前發(fā)出的等待用戶進程啟動的消息
  3. 通知用戶進程初始化android的運行環(huán)境,并創(chuàng)建application對象.執(zhí)行oncreate.
  4. 找到之前正在等待啟動的activity,然后繼續(xù)啟動他
 private final boolean attachApplicationLocked(IApplicationThread thread,int pid) {
    //把用戶進程傳來的ApplicationThread 和一個processRecord 綁定起來.
        ProcessRecord app;
        app = mPidsSelfLocked.get(pid);
        app.thread = thread;
        //移除AMS之前發(fā)出的等待10s內(nèi)用戶進程啟動的消息        
    mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
    //通知用戶進程初始化android的運行環(huán)境,在用戶端會初始化很多東西.并創(chuàng)建application對象.執(zhí)行oncreate.
     thread.bindApplication(processName, appInfo, providers,
                    app.instrumentationClass, profileFile, profileFd, profileAutoStop,
                    app.instrumentationArguments, app.instrumentationWatcher, testMode,
                    enableOpenGlTrace, isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
                    mCoreSettingsObserver.getCoreSettingsLocked());
      
      //找到之前正在等待啟動的activity,然后繼續(xù)啟動他
     ActivityRecord hr = mMainStack.topRunningActivityLocked(null);
     mMainStack.realStartActivityLocked(hr, app, true, true)
     
     //后面還有啟動Service.不看了
  }

總結(jié),用戶進程的啟動由AMS決定.AMS通過zygote來fork出用戶進程,用戶進程啟動后主動向AMS注冊.AMS再把這個用戶進程和一個ProcessRecord 綁定起來.同時回調(diào)用戶進程進行初始化.用戶進程在初始化資源,創(chuàng)建Application.并執(zhí)行onCreate.

ACtivityStack.realStartActivityLocked

此時,用戶進程已經(jīng)啟動完畢. ActivityRecord, TaskRecord,ProcessRecord 都已經(jīng)準備好了.用戶進程中Application已經(jīng)創(chuàng)建并執(zhí)行onCreate.繼續(xù)執(zhí)行Activity

  1. 通知用戶進程啟動Activity.
  2. 等待用戶進程Activity啟動,執(zhí)行完onResume后通知AMS,這里AMS也是超時等待,10s內(nèi)APP沒通知,就認為app啟動失敗.
 final boolean realStartActivityLocked(ActivityRecord r,
            ProcessRecord app, boolean andResume, boolean checkConfig){
   //processRecord 把ActivityRecord 保存起來.         
   app.activities.add(r);
    //通知用戶進程啟動activity.通過ApplicationThread進行binder通信來實現(xiàn).
     app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info,
                    new Configuration(mService.mConfiguration),
                    r.compat, r.icicle, results, newIntents, !andResume,
                    mService.isNextTransitionForward(), profileFile, profileFd,
                    profileAutoStop);
    //等待用戶進程Activity啟動,執(zhí)行完onResume后通知AMS,這里AMS也是超時等待,10s內(nèi)APP沒通知,就認為app啟動失敗.                
     completeResumeLocked(r);
}

此時AMS又通知Activity進行啟動.我們要再次來到用戶進程.ApplicationThread 通過發(fā)消息.傳遞給ActivityThread.handleLaunchActivity方法

ActivityThread.handleLaunchActivity

  1. 執(zhí)行activity的onCreat方法 和onRestart方法
  2. 執(zhí)行activity的onResume方法.同時用handle發(fā)送了一個Idler消息.Idler消息會在向AMS發(fā)送通知.說明用戶進程里Activity 已經(jīng)成功顯示.AMS就去掉上一步的超時等待的消息
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //執(zhí)行activity的onCreat方法 和onRestart方法
     Activity a = performLaunchActivity(r, customIntent);
     //執(zhí)行activity的onResume方法.同時用handle發(fā)送了一個Idler消息.Idler消息會在向AMS發(fā)送通知.說明用戶進程里Activity 已經(jīng)成功顯示.AMS就去掉上一步的超時等待的消息.
      handleResumeActivity(r.token, false, r.isForward);
}

AMS收到這個消息后.就知道APP已經(jīng)成功啟動了.會做一些收尾的工作.然后結(jié)束那些處于stop和finish狀態(tài)的activity.

ActivityStack.activityIdleInternal

  1. 處理所有的stop和finish狀態(tài)的activity
 stopActivityLocked(r);
 destroyActivityLocked(r, true, false, "finish-idle");

到此. 我們經(jīng)歷了一個activity的啟動. 首先是權(quán)限的檢查,然后為這個新的activity創(chuàng)建ActivityRecord.ProcessRecord,TaskRecord, 然后在啟動用戶線程,創(chuàng)建Application,用戶線程在綁定到AMS,AMS在把ProcessRecord和ApplicationThread進行綁定然后通知用戶進程初始化安卓環(huán)境.AMS在通知ActivityThread 進行l(wèi)aunchActivity. 用戶進程就創(chuàng)建這個Activity.執(zhí)行onCreate,onStart,onResume. 然后在onResume后通知Ams啟動完畢.Ams在做一些收尾工作.

image-20200307180845839

ActivityStack.

之前我們在ActivityThread..resumeTopActivityLocked的時候,看到,會先中止舊的activity的顯示,在創(chuàng)建新的activity.當時略過去.現(xiàn)在繼續(xù)看舊activity的暫停過程

  1. 拿到要暫停的activityRecord
  2. 通知該進程,暫停這個activity
  3. 發(fā)送超時提醒,等待用戶端暫停后通知AMS
        //拿到正在顯示的activity,也就是要暫停的那個
    ActivityRecord prev = mResumedActivity;
    //同理.通過用戶進程的ApplicationThread來通知用戶進程,這個activity要暫停
       prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                        userLeaving, prev.configChangeFlags);
      //同樣設(shè)置延時等待,等用戶進程暫停activity后的通知,這個實際是500毫秒.
      Message msg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
                mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT);

這就又進入了用戶進程的執(zhí)行情況

ActivityThread.handlePauseActivity

  1. 調(diào)用戶的onPause
  2. 通知AMS. 用戶端onPause完成
    //調(diào)用戶的onPause
  r.activity.mConfigChangeFlags |= configChanges;
  performPauseActivity(token, finished, r.isPreHoneycomb());
    
  try {     //通知AMS. 用戶端onPause完成
      ActivityManagerNative.getDefault().activityPaused(token);
  } catch (RemoteException ex) {
  }

ActivityStack.completePauseLocked

  1. 跳轉(zhuǎn)這暫停的activityinfo 在activityStack中各個狀態(tài)里邊中的情況,把他加入mStoppingActivities里.這會在新ActivityRecord 對應的activity完全啟動完成后.執(zhí)行舊activity的onstop.
  2. 因此我們看到.舊activity的onPaus是早于新activity的onCreate.但是舊activity的onStop要等新activity的onResume 執(zhí)行完成后才執(zhí)行.
ActivityRecord prev = mPausingActivity;
mStoppingActivities.add(prev);
mWaitingVisibleActivities.remove(prev);
image-20200307183446289

最后在附一個自己整理的流程圖

ams124
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末梁剔,一起剝皮案震驚了整個濱河市虽画,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌憾朴,老刑警劉巖狸捕,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異众雷,居然都是意外死亡灸拍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門砾省,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸡岗,“玉大人,你說我怎么就攤上這事编兄⌒裕” “怎么了?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵狠鸳,是天一觀的道長揣苏。 經(jīng)常有香客問我,道長件舵,這世上最難降的妖魔是什么卸察? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮铅祸,結(jié)果婚禮上坑质,老公的妹妹穿的比我還像新娘。我一直安慰自己临梗,他們只是感情好涡扼,可當我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布晒奕。 她就那樣靜靜地躺著氏捞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪危号。 梳的紋絲不亂的頭發(fā)上茫经,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天巷波,我揣著相機與錄音,去河邊找鬼卸伞。 笑死抹镊,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的荤傲。 我是一名探鬼主播垮耳,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了终佛?” 一聲冷哼從身側(cè)響起俊嗽,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎铃彰,沒想到半個月后绍豁,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡牙捉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年竹揍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邪铲。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡芬位,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出带到,到底是詐尸還是另有隱情昧碉,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布揽惹,位于F島的核電站被饿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏搪搏。R本人自食惡果不足惜锹漱,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望慕嚷。 院中可真熱鬧,春花似錦毕泌、人聲如沸喝检。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挠说。三九已至,卻和暖如春愿题,著一層夾襖步出監(jiān)牢的瞬間损俭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工潘酗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留杆兵,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓仔夺,卻偏偏與公主長得像琐脏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容