1. 程式人生 > >Android面試題(31)-App啟動流程

Android面試題(31)-App啟動流程

先貼個連結,總結的挺全面

在看這篇文章之前,希望先看完我的之前的部落格 android面試(6)-Binder機制,因為關於App啟動流程設計很多Binder通訊;

先將“三個程序”,“六個大類”進行介紹:

三個程序:

Launcher程序:整個App啟動流程的起點,負責接收使用者點選螢幕事件,它其實就是一個Activity,裡面實現了點選事件,長按事件,觸控等事件,可以這麼理解,把Launcher想象成一個總的Activity,螢幕上各種App的Icon就是這個Activity的button,當點選Icon時,會從Launcher跳轉到其他頁面;

SystemServer程序:這個程序在整個的Android程序中是非常重要的一個,地位和Zygote等同,它是屬於Application Framework層的,Android中的所有服務,例如AMS, WindowsManager, PackageManagerService等等都是由這個SystemServer fork出來的。所以它的地位可見一斑。

App程序:你要啟動的App所執行的程序;

六個大類:

ActivityManagerService:(AMS)AMS是Android中最核心的服務之一,主要負責系統中四大元件的啟動、切換、排程及應用程序的管理和排程等工作,其職責與作業系統中的程序管理和排程模組相類似,因此它在Android中非常重要,它本身也是一個Binder的實現類。

Instrumentation:監控應用程式和系統的互動;

ActivityThread:應用的入口類,通過呼叫main方法,開啟訊息迴圈佇列。ActivityThread所在的執行緒被稱為主執行緒;

ApplicationThread:ApplicationThread提供Binder通訊介面,AMS則通過代理呼叫此App程序的本地方法

ActivityManagerProxy:AMS服務在當前程序的代理類,負責與AMS通訊。

ApplicationThreadProxy:ApplicationThread在AMS服務中的代理類,負責與ApplicationThread通訊。

可以說,啟動的流程就是通過這六個大類在這三個程序之間不斷通訊的過程;

我先簡單的梳理一下app的啟動的步驟:

(1)啟動的起點發生在Launcher活動中,啟動一個app說簡單點就是啟動一個Activity,那麼我們說過所有元件的啟動,切換,排程都由AMS來負責的,所以第一步就是Launcher響應了使用者的點選事件,然後通知AMS

(2)AMS得到Launcher的通知,就需要響應這個通知,主要就是新建一個Task去準備啟動Activity,並且告訴Launcher你可以休息了(Paused);

(3)Launcher得到AMS讓自己“休息”的訊息,那麼就直接掛起,並告訴AMS我已經Paused了;

(4)AMS知道了Launcher已經掛起之後,就可以放心的為新的Activity準備啟動工作了,首先,APP肯定需要一個新的程序去進行執行,所以需要建立一個新程序,這個過程是需要Zygote參與的,AMS通過Socket去和Zygote協商,如果需要建立程序,那麼就會fork自身,建立一個執行緒,新的程序會匯入ActivityThread類,這就是每一個應用程式都有一個ActivityThread與之對應的原因;

(5)程序建立好了,通過呼叫上述的ActivityThread的main方法,這是應用程式的入口,在這裡開啟訊息迴圈佇列,這也是主執行緒預設繫結Looper的原因;

(6)這時候,App還沒有啟動完,要永遠記住,四大組建的啟動都需要AMS去啟動,將上述的應用程序資訊註冊到AMS中,AMS再在堆疊頂部取得要啟動的Activity,通過一系列鏈式呼叫去完成App啟動;

下面這張圖很好的描述了上面的六大步:

接下來看看詳細看看這幾個步驟:

(1)Launcher響應使用者點選,通知AMS

Launcher呼叫startActivity()方法,而startActivity()最終也是呼叫startActivityForResult();

@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
    if (options != null) {
        startActivityForResult(intent, -1, options);
    } else {
        // Note we want to go through this call for compatibility with
        // applications that may have overridden the method.
        startActivityForResult(intent, -1);
    }
}
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
        @Nullable Bundle options) {
    if (mParent == null) {
        options = transferSpringboardActivityOptions(options);
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                ar.getResultData());
        }
       ......
}

在其中呼叫了Instrumentation的execstartActivity方法:

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
        .....
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess(who);
        int result = ActivityManager.getService()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

這裡呼叫了ActivityManager.getService方法返回的就是ActivityManagerProxy,用於與AMS通訊,這樣通過這些一系列的方法呼叫,就把Launcher程序和AMS程序進行通訊了一次;

(2)AMS響應Launcher程序請求

主要是通過ActivityStackSupervisor:startActivityUncheckedLocked去給Activity申請Task的;此方法經過intent的標誌值設定,通過findTaskLocked函式來查詢存不存這樣的Task,這裡返回的結果是null,即intentActivity為null,因此,需要建立一個新的Task來啟動這個Activity。現在處理堆疊頂端的Activity是Launcher,與我們即將要啟動的MainActivity不是同一個Activity,建立了一個新的Task裡面來啟動這個Activity。

經過棧頂檢測,則需要將Launcher推入Paused狀態,才可以啟動新的Activity。後續則呼叫至ActivityStack:startPausingLocked;此方法裡有prev.app.thread是一個ApplicationThread物件的遠端介面,通過呼叫這個遠端介面的schedulePauseActivity來通知Launcher進入Paused狀態。至此,AMS對Launcher的請求已經響應,這是我們發現又通過Binder通訊回撥至Launcher程序;

(3)Launcher程序掛起Launcher,再次通知AMS

這部分Launcher的ActivityThread處理頁面Paused並且再次通過ActivityManagerProxy通知AMS。

(4)AMS建立新的程序

建立新程序的時候,AMS會儲存一個ProcessRecord資訊,如果應用程式中的AndroidManifest.xml配置檔案中,我們沒有指定Application標籤的process屬性,系統就會預設使用package的名稱。每一個應用程式都有自己的uid,因此,這裡uid + process的組合就可以為每一個應用程式建立一個ProcessRecord。

這裡主要是呼叫Process:start介面來建立一個新的程序,新的程序會匯入android.app.ActivityThread類,並且執行它的main函式,這就是每一個應用程式都有一個ActivityThread例項來對應的原因。

先判斷是否有相應的 ProcessRecord,如果不存在,就需要新建程序,這個程序就是相應的應用程序。ActivityManagerService 通過 Socket 通訊的方式和 Zygote 進行協商,Zygote 在其監聽的 /dev/socket/zygote socket 中發現有需要建立程序的請求後,會 fork 自身,並返回相應的 Process Id。這個 Process 會進行相應的初始化,使得其具備與系統服務進行 IPC 通訊的能力;

(5)應用程序初始化

我們來看Activity的main函式,這裡綁定了主執行緒的Looper,並進入訊息迴圈,大家應該知道,整個Android系統是訊息驅動的,這也是為什麼主執行緒預設繫結Looper的原因:

public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
    SamplingProfilerIntegration.start();

    // CloseGuard defaults to true and can be quite spammy.  We
    // disable it here, but selectively enable it later (via
    // StrictMode) on debug builds, but using DropBox, not logs.
    CloseGuard.setEnabled(false);

    Environment.initForCurrentUser();

    // Set the reporter for event logging in libcore
    EventLogger.setReporter(new EventLoggingReporter());

    // Make sure TrustedCertificateStore looks in the right place for CA certificates
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);

    Process.setArgV0("<pre-initialized>");

    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

attach函式最終呼叫了ActivityManagerService的遠端介面ActivityManagerProxy的attachApplication函式,傳入的引數是mAppThread,這是一個ApplicationThread型別的Binder物件,它的作用是AMS與應用程序進行程序間通訊的。

(6)在AMS中註冊應用程序,啟動啟動棧頂頁面

前面我們提到了AMS負責系統中四大元件的啟動、切換、排程及應用程序的管理和排程等工作,通過上一個流程我們知道應用程序建立後通過Binder驅動與AMS產生互動,此時AMS則將應用程序建立後的資訊進行了一次註冊,如果拿Windows系統程式註冊到的登錄檔來理解這個過程,可能會更形象一些。

mMainStack.topRunningActivityLocked(null)從堆疊頂端取出要啟動的Activity,並在realStartActivityLockedhan函式中通過ApplicationThreadProxy調回App程序啟動頁面。

此時在App程序,我們可以看到,經過一些列的呼叫鏈最終呼叫至MainActivity:onCreate函式,之後會呼叫至onResume,而後會通知AMS該MainActivity已經處於resume狀態。至此,整個啟動流程告一段落。

上面講的是activity的啟動流程,下面要說的是,android系統的啟動流程,通俗來說,按下電源鍵,開機的過程系統做了些什麼:

1.當系統載入程式啟動Linux核心,核心會載入各種資料結構,和驅動程式,載入完畢之後,Android系統開始啟動並載入第一個使用者級別的程序:init(system/core/init/Init.c)

我們來看一看這個Init.c程式碼,看main函式

int main(int argc, char **argv)
{
     ...
    //執行Linux指令
    mkdir("/dev", 0755);
    mkdir("/proc", 0755);
    mkdir("/sys", 0755);

    ...
    //解析執行init.rc配置檔案
    init_parse_config_file("/init.rc");
    ...
}

主要建立了一些目錄,並且載入執行了一個init.rc的檔案,我們來看看這個init.rc

2.在system\core\rootdir\Init.rc中定義好的指令都會開始執行,其中執行了很多bin指令,啟動系統服務

//啟動孵化器程序,此程序是Android系統啟動關鍵服務的一個母程序
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
socket zygote stream 666
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd

裡面啟動了一個app_process,我們看看這是什麼:

3.在appprocess資料夾下找到appmain.cpp,檢視main函式,發現以下程式碼

int main(int argc, const char* const argv[])
{
    ...
    //啟動一個系統服務:ZygoteInit
    runtime.start("com.android.internal.os.ZygoteInit",startSystemServer);
    ...
}

仍然是為啟動一個系統服務做準備,我們繼續跟下去看看:

4.在ZygoteInit.java中,檢視main方法

 public static void main(String argv[]) {
    ...
    //載入Android系統需要的類
    preloadClasses();
    ...
    if (argv[1].equals("true")) {
        //呼叫方法啟動一個系統服務
        startSystemServer();
    }
    ...
}

在這裡主要是先載入了android系統所需要的類,然後啟動系統服務,繼續跟下去:

5.startSystemServer()方法的方法體

String args[] = {
    "--setuid=1000",
    "--setgid=1000",
    "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003",
    "--capabilities=130104352,130104352",
    "--runtime-init",
    "--nice-name=system_server",
    "com.android.server.SystemServer",
};

...
//分叉啟動上面字串陣列定義的服務
 pid = Zygote.forkSystemServer(
 parsedArgs.uid, parsedArgs.gid,
 parsedArgs.gids, debugFlags, null,
 parsedArgs.permittedCapabilities,
 parsedArgs.effectiveCapabilities);

 6.SystemServer服務被啟動

public static void main(String[] args) {
    ...
    //載入動態連結庫
     System.loadLibrary("android_servers");
    //執行連結庫裡的init1方法
    init1(args);
    ...
}

7.動態連結庫檔案和java類包名相同,找到com_android_server_SystemServer.cpp檔案

在com_android_server_SystemServer.cpp檔案中,找到了

static JNINativeMethod gMethods[] = {
    /* name, signature, funcPtr */
    //給init1方法對映一個指標,呼叫system_init方法
    { "init1", "([Ljava/lang/String;)V", (void*) android_server_SystemServer_init1 },
};

 8.android_server_SystemServer_init1方法體中呼叫了system_init(),system_init()沒有方法體

在system_init.cpp檔案中找到system_init()方法,方法體中 //執行了SystemServer.java的init2方法 runtime->callStatic("com/android/server/SystemServer", "init2");

回到SystemServer.java,在init2的方法體中

//啟動一個服務執行緒
Thread thr = new ServerThread();
thr.start();

 9.在ServerThread的run方法中

//準備訊息輪詢器
Looper.prepare();
...
//啟動大量的系統服務並把其逐一新增至ServiceManager
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
...
//呼叫systemReady,準備建立第一個activity
 ((ActivityManagerService)ActivityManagerNative.getDefault())
        .systemReady(new Runnable(){
        ...
});

10.在ActivityManagerService.java中,有systemReady方法,方法體裡找到

//檢測任務棧中有沒有activity,如果沒有,建立Launcher
mMainStack.resumeTopActivityLocked(null);

11.在ActivityStack.java中,方法resumeTopActivityLocked

// Find the first activity that is not finishing.
ActivityRecord next = topRunningActivityLocked(null);
...
if (next == null) {
    // There are no more activities!  Let's just start up the
    // Launcher...
    if (mMainStack) {
        return mService.startHomeActivityLocked();
    }
}
...