APP的啟動流程梳理

語言: CN / TW / HK

前言

整整一週,利用晚上下班時間梳理了桌面點選APP到開啟的流程,可以說看似簡簡單單的一個操作,裡邊的過程相當複雜,各種程序間通訊等等,下面我們一起看看整個流程是怎麼樣的。

正文

我們先通過一張圖來看整個流程的一個概覽:

在這裡插入圖片描述

startActivity

桌面點選app到開啟,涉及了三個程序,分別如下:

Launcher程序:它是一個Activity,可以把桌面看成是一個app,裡邊有多個啊、其他app的入口,當點選app圖示時,就會去啟動對應的app,並且跳轉至頁面。

SystemServer程序:在Android系統中有著重要的作用,由Zygote程序fork出來,許多重要的服務,都是在此程序開啟的,例如ActivityManagerService、InputManagerService和WindowManagerService等等。

APP程序:我們要啟動的app的程序。

startActivity

首先我們從startActivity開始,startActivity最終會呼叫startActivityForResult:

java 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); //... } else { //... } }

在這裡我們先了解一下Instrumentation,每個Activity都會持有一個Instrumentation引用,整個程序只會有一個Instrumentation的例項,它主要是完成對Application和Activity初始化和生命週期的工具類。

我們可以看到,裡邊呼叫了mInstrumentation的execStartActivity方法,其中的核心程式碼如下:

java public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { //... try { //... int result = ActivityTaskManager.getService().startActivity(whoThread, who.getBasePackageName(), who.getAttributionTag(), 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; }

ActivityTaskManager.getService()返回的是一個ActivityManagerProxy,它就是用來與AMS進行通訊的,AMS是執行在system_server程序中的,這一次的呼叫,實際是通過Binder的方式,呼叫到了AMS的startActivity方法。

fork建立新程序

呼叫了ActivityManagerService的startActivity方法後,經過一系列的呼叫,來到了startProcesslocked方法,然後會通過Process.start()方法,這次使用的是socket通訊方式向zygote程序傳送一個建立新程序的請求,也就是請求Zygote去建立App程序。

Zygote程序監聽到有建立新程序的請求後,便會fork新的程序,並返回對應的pid。新程序建立後,然後會執行ActivityThread的main()方法。

這裡說一下Zygote程序:由init程序fork出來的,當建立app程序時,都是由zygote程序fork而來的。

不知你會不會有兩個疑問:

為什麼SystemServer程序與Zegote程序通訊不是使用Binder而是使用Socket?下面整理了網友的幾點回答。

  • zegote比serviceManager先啟動,這點從先後順序看,沒有serviceManager可以註冊,沒法用Binder。
  • 假設它們誰先啟動這個順序不確定,但是如果serviceManager先啟動,但沒法保證它先初始化完。
  • 在安全性上,socket的所有者是root,group是system,只有系統許可權使用者才能進行讀寫。

為什麼APP程序需要由Zegote程序fork出來?

我們知道每個APP都執行在獨立的Dalvik虛擬機器中,如果每啟動一個APP就得去單獨啟動跟初始化,那麼是比較耗時的。Zegote程序會把已經載入好的虛擬機器程式碼和記憶體資訊共享,通過它fork會起到一個預載入作用,加快了app的啟動。

繫結Application

建立執行緒後,便會執行ActivityThread的main函式,main函式裡邊會啟動主執行緒的Looper。在我們初學Java的時候可以知道,main函式是一個應用程式的入口。

main函式裡邊會呼叫ActivityThread的attach方法。

java //由於在main方法裡邊呼叫傳進來的system為false,所以我們只看第一個分支。 private void attach(boolean system, long startSeq) { //... if (!system) { final IActivityManager mgr = ActivityManager.getService(); try { mgr.attachApplication(mAppThread, startSeq); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } else { //... } }

attach方法中,會遠端呼叫ActivityManagerService的attachApplication方法,ActivityManagerService最後會通過遠端呼叫ActivityThread的bindApplication,bindApplication會發送一個BIND_APPLICATION的訊息,

java public final void bindApplication(String processName, ApplicationInfo appInfo, ProviderInfoList providerList, ComponentName instrumentationName,/*省略n個引數*/) { //... sendMessage(H.BIND_APPLICATION, data); }

接收到此訊息後,通過handleBindApplication方法進行處理,然後呼叫LoadedApk的makeApplication方法,由Instrumentation載入Application例項出來,

```java public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) { if (mApplication != null) { return mApplication; }

//...

Application app = null;

//...

try {
    final java.lang.ClassLoader cl = getClassLoader();
   //...
    app = mActivityThread.mInstrumentation.newApplication(
            cl, appClass, appContext);
    appContext.setOuterContext(app);
} catch (Exception e) {
    //...
}

//... mApplication = app;

if (instrumentation != null) {
    try {
        instrumentation.callApplicationOnCreate(app);
    } catch (Exception e) {
        //...
    }
}
return app;

} ```

newApplication方法裡邊會去加載出Application物件,並且呼叫它的attach方法。

java public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { Application app = getFactory(context.getPackageName()) .instantiateApplication(cl, className); app.attach(context); return app; }

建立完之後,回到ActivityThread的handleBindApplication方法,會通過Instrumentation去呼叫Application的onCreate方法。

java try { mInstrumentation.callApplicationOnCreate(app); } catch (Exception e) { //... }

到這裡Application的建立就完成了。

啟動Activity

經過一連串的呼叫,最後會向H傳送一個訊息,這裡的H是一個Handler,由於版本不同,所以這裡會有不一樣,它們最終都會呼叫到handleLaunchActivity方法:

java public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) { //... final Activity a = performLaunchActivity(r, customIntent); //... return a; }

performLaunchActivity方法裡邊會建立Activity,並且走onCreate

```java private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { //...

ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
    java.lang.ClassLoader cl = appContext.getClassLoader();
    activity = mInstrumentation.newActivity(
            cl, component.getClassName(), r.intent);

//... } catch (Exception e) { //... }

try {
    //...設定一些引數等,還有attach進Application
        if (r.isPersistable()) {
            mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {
            mInstrumentation.callActivityOnCreate(activity, r.state);
        }
        //...
    }
    r.setState(ON_CREATE);

} catch (SuperNotCalledException e) {
    throw e;
} catch (Exception e) {
    //...
}

return activity;

} ```

可以看到,又有Instrumentation的身影,此方法裡邊通過mInstrumentation去建立Activity,然後設定引數、校驗等操作後,再繼續呼叫callActivityOnCreate方法,裡邊會去呼叫Activity的onCreate方法,至此,Activity被建立好了。

接下來就是繼續通過Handler,然後去呼叫Activity的onStart、onResume,到這裡,Activity就可見了。

小結

最後我們回顧一下整個流程:

  1. 在桌面點選app圖示,Launcher響應點選事件,然後經過呼叫,通過Binder的方式告訴在system_server程序中的ActivityManagerService去startActivity
  2. ActivityManagerService收到呼叫後,便會去請求建立一個新的程序,它通過Socket的方式,告訴Zygote程序去啟動一個新的程序。
  3. 新的程序啟動後,會執行ActivityThread的main方法,這是程式的入口,並且會開始Looper。
  4. 在main中,會去請求ActivityManagerService進行attach Application,再經過一系列的呼叫,會回到app程序,建立Application,並且讓Application進行attach。
  5. Application建立繫結完,便開始建立Activity,由AMS告訴APP程序去scheduleLaunchActivity,APP程序會發送一個Handler的訊息,收到這個訊息後由Instrumentation去建立Activity,接著繼續去呼叫Activity的onCreate、onStart和onResume的生命週期,至此,從桌面點選APP的圖示到APP啟動至可見已完成。

結語

到這裡我們便把APP的啟動流程過了一遍,看似很簡單的一個操作,實際上系統幫我們做了很多的事情。當然在原始碼上不同版本會存在不一樣,但它們的流程基本是一樣的,只是一些方法或者細節做了改變。

我正在參與掘金技術社群創作者簽約計劃招募活動,點選連結報名投稿