startActivity 啟動過程分析

整個 startActivity 的流程分為 3 大部分宣脉,也涉及 3 個進程之間的交互:

image.png
1. ActivityA --> ActivityManagerService(簡稱 AMS)
2. ActivityManagerService --> ApplicationThread
3. ApplicationThread --> Activity

1.ActivityA --> ActivityManagerService 階段

ActivityA --> ActivityManagerService 階段

Activity 的 startActivity

Activity 的 startActivity

最終調(diào)用到了startActivityForResult,傳入-1表示不需要獲取startActivity的結(jié)果。

Activity 的 startActivityForResult

Activity 的 startActivityForResult

1.Instrumentation 類主要用來監(jiān)控應用程序與系統(tǒng)交互民鼓。
2.mMainThread是ActivityThread,可以理解為一個進程润脸,初始啟動Activity所在的進程怔球。
3.通過 mMainThread 獲取一個 ApplicationThread 的引用,這個引用就是用來實現(xiàn)進程間通信的,具體來說就是 AMS 所在系統(tǒng)進程通知應用程序進程進行的一系列操作吴攒。

Instrumentation 的 execStartActivity

Instrumentation 的 execStartActivity

在 Instrumentation.execStartA ctivity方法中张抄,先通過 ActivityManger.getService 獲取 AMS 的實例,然后調(diào)用其 startActivity 方法洼怔,實際上這里就是通過 AIDL 來調(diào)用 AMS 的 startActivity 方法署惯,至此,startActivity 的工作重心成功地從進程 A 轉(zhuǎn)移到了系統(tǒng)進程 AMS 中镣隶。

2.ActivityManagerService --> ApplicationThread

AMS-->ApplicationThread 流程里面實際做了兩件事:
1.綜合處理 launchMode 和 Intent 中的 Flag 標志位极谊,并根據(jù)處理結(jié)果生成一個目標 Activity B 的對象(ActivityRecord)。
2.判斷是否需要為目標 Activity B 創(chuàng)建一個新的進程(ProcessRecord)安岂、新的任務棧(TaskRecord)轻猖。

AMS 的 startActivity

AMS 的 startActivity

經(jīng)過多次調(diào)用,最終調(diào)用到startActivityAsUser方法中域那,通過obtainStarter方法獲取ActivityStarter 類型的對象咙边,然后調(diào)用其 execute 方法。在 execute 方法中次员,會再次調(diào)用其內(nèi)部的 startActivityMayWait 方法败许。

ActivityStarter 的 startActivityMayWait

ActivityStarter 的 startActivityMayWait

1.通過ActivityStackSupervisor獲取ResolveInfo信息。resolveIntent 中實際上是調(diào)用系統(tǒng)PackageManagerService 來獲取最佳 Activity淑蔚。
有時候我們通過隱式 Intent 啟動 Activity 時市殷,系統(tǒng)中可能存在多個 Activity 可以處理 Intent,此時會彈出一個選擇框讓用戶選擇具體需要打開哪一個 Activity 界面刹衫,就是此處的邏輯處理結(jié)果醋寝。
2.通過ActivityStackSupervisor獲取ActivityInfo信息。
3.調(diào)用重載方法startActivity啟動Activity带迟。

從圖中可以看出獲取目標 Activity 信息的操作由 mSupervisor 來實現(xiàn)音羞,它是 ActivityStackSupervisor 類型,從名字也能猜出它主要是負責 Activity 所處棧的管理類仓犬。

startActivity 方法黄选,而最終會調(diào)用的 ActivityStarter 中的 startActivityUnchecked 方法來獲取啟動 Activity 的結(jié)果。

ActivityStarter 的 startActivityUnchecked

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            ActivityRecord[] outActivity) {
...
//計算啟動 Activity 的 Flag 值
 computeLaunchingTaskFlags();
...
//處理 Task 和 Activity 的進棧操作
  mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,mOptions);
...
//啟動棧中頂部的 Activity
 mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,mOptions);
...
}

computeLaunchingTaskFlags方法如下:

image.png

這個計算啟動Activity的Flag值婶肩,不同的 Flag 決定了啟動 Activity 最終會被放置到哪一個 Task 集合中办陷。

  1. mInTask 是 TaskRecord 類型,此處為 null律歼,代表 Activity 要加入的棧不存在民镜,因此需要判斷是否需要新建 Task。
  2. mSourceRecord 的類型 ActivityRecord 類型险毁,它是用來描述“初始 Activity”制圈, ActivityA 啟動了ActivityB们童,ActivityA 就是初始 Activity。當我們使用 Context 或者 Application 啟動 Activity 時鲸鹦, SourceRecord 為 null慧库。
  3. 表示初始 Activity 如果是在 SingleInstance 棧中的 Activity,這種需要添加 NEW_TASK 的標識馋嗜。SingleInstance 棧只能允許保存一個 Activity齐板。
  4. 如果 Launch Mode 設(shè)置了 singleTask 或 singleInstance,則也要創(chuàng)建一個新棧葛菇。

ActivityStack 的 startActivityLocked

ActivityStack 的 startActivityLocked

調(diào)用 insertTaskAtTop 方法嘗試將 Task 和 Activity 入棧甘磨。如果 Activity 是以 newTask 的模式啟動或者 TASK 堆棧中不存在該 Task id,則 Task 會重新入棧眯停,并且放在棧的頂部济舆。需要注意的是:Task 先入棧,之后才是 Activity 入棧莺债,它們是包含關(guān)系滋觉。


image.png

一個ActivityRecord對應一個Activity,保存了一個Activity的所有信息; 但是一個Activity可能會有多個ActivityRecord,因為Activity可以被多次啟動齐邦,這個主要取決于其啟動模式椎侠。

一個TaskRecord由一個或者多個ActivityRecord組成,這就是我們常說的任務棧侄旬,具有后進先出的特點。

ActivityStack則是用來管理TaskRecord的煌妈,包含了多個TaskRecord儡羔。
參考Activity啟動流程

ActivityStackSupervisor的resumeFocusedStackTopActivityLocked

image.png

image.png
image.png

經(jīng)過一系列調(diào)用,最終代碼有回到了ActivityStackSupervisor 中的 startSpecificActivityLocked 方法璧诵。

ActivityStackSupervisor 的 startSpecificActivityLocked

image.png

1.根據(jù)進程名稱和 Application 的 uid 來判斷目標進程是否已經(jīng)創(chuàng)建汰蜘,如果沒有則代表進程未創(chuàng)建。
2.不管是目標進程已經(jīng)存在還是新建目標進程之宿,最終都會調(diào)用圖中紅線標記的 realStartActivityLocked 方法來執(zhí)行啟動 Activity 的操作族操。
3.調(diào)用 AMS 創(chuàng)建 Activity 所在進程。

ActivityStackSupervisor 的 realStartActivityLocked


image.png
  1. 處創(chuàng)建 Activity 啟動事務比被,并傳入 app.thread 參數(shù)色难,它是 ApplicationThread 類型。在上文 startActivity 階段已經(jīng)提過 ApplicationThread 是為了實現(xiàn)進程間通信的等缀,是 ActivityThread 的一個內(nèi)部類枷莉。
  2. 處執(zhí)行 Activity 啟動事務。

Activity 啟動事務的執(zhí)行是由 ClientLifecycleManager 來完成的尺迂。
ClientLifecycleManager.scheduleTransaction

image.png

可以看出實際上是調(diào)用了啟動事務 ClientTransaction 的 schedule 方法笤妙,而這個 transaction 實際上是在創(chuàng)建 ClientTransaction 時傳入的 app.thread 對象冒掌,也就是 ApplicationThread。


image.png
image.png
  1. 傳入的 app.thread 會賦值給 ClientTransaction 的成員變量 mClient蹲盘,ClientTransaction 會調(diào)用 mClient.scheduleTransaction(this) 來執(zhí)行事務股毫。
  2. app.thread 是 ActivityThread 的內(nèi)部類 ApplicationThread,所以事務最終是調(diào)用 app.thread 的 scheduleTransaction 執(zhí)行召衔。
  3. 到這為止 startActivity 操作就成功地從 AMS 轉(zhuǎn)移到了另一個進程 B 中的 **ApplicationThread **中铃诬,剩下的就是 AMS 通過進程間通信機制通知 ApplicationThread 執(zhí)行 ActivityB 的生命周期方法。

3.ApplicationThread -> Activity

剛才我們已近分析了 AMS 將啟動 Activity 的任務作為一個事務 ClientTransaction 去完成薄嫡,在 ClientLifecycleManager 中會調(diào)用 ClientTransaction的schedule() 方法氧急,如下:


image.png

mClient 是一個 IApplicationThread 接口類型,具體實現(xiàn)是 ActivityThread 的內(nèi)部類 ApplicationThread毫深。因此后續(xù)執(zhí)行 Activity 生命周期的過程都是由 ApplicationThread 指導完成的吩坝,scheduleTransaction 方法如下:


image.png

調(diào)用了ActivityThread 的 scheduleTransaction 方法。但是這個方法實際上是在 ActivityThread 的父類 ClientTransactionHandler 中實現(xiàn)哑蔫,具體如下:
image.png

調(diào)用 sendMessage 方法钉寝,向 Handler 中發(fā)送了一個 EXECUTE_TRANSACTION 的消息,并且 Message 中的 obj 就是啟動 Activity 的事務對象闸迷。而這個 Handler 的具體實現(xiàn)是 ActivityThread 中的 mH 對象嵌纲。具體如下:

public final class ActivityThread extends ClientTransactionHandler {
      final H mH = new H();
      class H extends Handler {
            ...
               case EXECUTE_TRANSACTION:
                        return "EXECUTE_TRANSACTION";
            ...

             public void handleMessage(Message msg) {
                    case EXECUTE_TRANSACTION:
                    final ClientTransaction transaction = (ClientTransaction) msg.obj;
                    mTransactionExecutor.execute(transaction);
                    ...
            }  
    }
}

最終調(diào)用了事務的 execute 方法,execute 方法如下:

public void execute(ClientTransaction transaction) {
        final IBinder token = transaction.getActivityToken();
        log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);

        executeCallbacks(transaction);

        executeLifecycleState(transaction);
        mPendingActions.clear();
        log("End resolving transaction");
    }

public void executeCallbacks(ClientTransaction transaction) {
    final int size = callbacks.size();
        for (int i = 0; i < size; ++i) {
            final ClientTransactionItem item = callbacks.get(i);
            log("Resolving callback: " + item);
            final int postExecutionState = item.getPostExecutionState();
            final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
                    item.getPostExecutionState());
            if (closestPreExecutionState != UNDEFINED) {
                cycleToPath(r, closestPreExecutionState);
            }

            item.execute(mTransactionHandler, token, mPendingActions);
            item.postExecute(mTransactionHandler, token, mPendingActions);
            if (r == null) {
                // Launch activity request will create an activity record.
                r = mTransactionHandler.getActivityClient(token);
            }

            if (postExecutionState != UNDEFINED && r != null) {
                // Skip the very last transition and perform it by explicit state request instead.
                final boolean shouldExcludeLastTransition =
                        i == lastCallbackRequestingState && finalState == postExecutionState;
                cycleToPath(r, postExecutionState, shouldExcludeLastTransition);
            }
        }

}

在 executeCallback 方法中腥沽,會遍歷事務中的 callback 并執(zhí)行 execute 方法逮走,這些 callbacks 是何時被添加的呢?


image.png

在創(chuàng)建 ClientTransaction 時今阳,通過 addCallback 方法傳入了 Callback 參數(shù)师溅,從圖中可以看出其實是一個 LauncherActivityItem 類型的對象。

LaunchActivityItem 的 execute()

image.png

終于到了跟 Activity 生命周期相關(guān)的方法了盾舌,圖中 client 是 ClientTransationHandler 類型墓臭,實際實現(xiàn)類就是 ActivityThread。因此最終方法又回到了 ActivityThread妖谴。

ActivityThread 的 handleLaunchActivity
Activity 的生命周期方法就是在這個方法中有序執(zhí)行窿锉,具體如下:

 public Activity handleLaunchActivity(ActivityClientRecord r,
                                         PendingTransactionActions pendingActions, Intent customIntent) {
       //1
       WindowManagerGlobal.initialize();     
        //2
      final Activity a = performLaunchActivity(r, customIntent);    
    ...
}
 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
     try {
            //3
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
        ...
      }
      ...
            //4
           activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
        ...
            //5
          if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
}
  1. 初始化 Activity 的 WindowManager,每一個 Activity 都會對應一個“窗口膝舅。
  2. 調(diào)用 performLaunchActivity 創(chuàng)建并顯示 Activity嗡载。
  3. 通過反射創(chuàng)建目標 Activity 對象。
  4. 調(diào)用 attach 方法建立 Activity 與 Context 之間的聯(lián)系仍稀,創(chuàng)建 PhoneWindow 對象鼻疮,并與 Activity 進行關(guān)聯(lián)操作。
  5. 通過 Instrumentation 最終調(diào)用 Activity 的 onCreate 方法琳轿。

4.總結(jié)

Activity 的啟動過程主要涉及 3 個進程間的通信過程:
首先進程 A 通過 Binder 調(diào)用 AMS 的 startActivity 方法判沟。
然后 AMS 通過一系列的計算構(gòu)造目標 Intent耿芹,然后在 ActivityStack 與 ActivityStackSupervisor 中處理 Task 和 Activity 的入棧操作。
最后 AMS 通過 Binder 機制挪哄,調(diào)用目標進程中 ApplicationThread 的方法來創(chuàng)建并執(zhí)行 Activity 生命周期方法吧秕,實際上 ApplicationThread 是 ActivityThread 的一個內(nèi)部類,它的執(zhí)行最終都調(diào)用到了 ActivityThread 中的相應方法迹炼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末砸彬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子斯入,更是在濱河造成了極大的恐慌砂碉,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刻两,死亡現(xiàn)場離奇詭異增蹭,居然都是意外死亡,警方通過查閱死者的電腦和手機磅摹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進店門滋迈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人户誓,你說我怎么就攤上這事饼灿。” “怎么了帝美?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵碍彭,是天一觀的道長。 經(jīng)常有香客問我悼潭,道長庇忌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任女责,我火速辦了婚禮漆枚,結(jié)果婚禮上创译,老公的妹妹穿的比我還像新娘抵知。我一直安慰自己,他們只是感情好软族,可當我...
    茶點故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布刷喜。 她就那樣靜靜地躺著,像睡著了一般立砸。 火紅的嫁衣襯著肌膚如雪掖疮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天颗祝,我揣著相機與錄音浊闪,去河邊找鬼恼布。 笑死,一個胖子當著我的面吹牛搁宾,可吹牛的內(nèi)容都是我干的折汞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼盖腿,長吁一口氣:“原來是場噩夢啊……” “哼爽待!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起翩腐,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤鸟款,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后茂卦,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體何什,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年疙筹,在試婚紗的時候發(fā)現(xiàn)自己被綠了富俄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡而咆,死狀恐怖霍比,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情暴备,我是刑警寧澤悠瞬,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站涯捻,受9級特大地震影響浅妆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜障癌,卻給世界環(huán)境...
    茶點故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一凌外、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧涛浙,春花似錦康辑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至我注,卻和暖如春按咒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背但骨。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工励七, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留智袭,地道東北人。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓掠抬,卻偏偏與公主長得像补履,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子剿另,可洞房花燭夜當晚...
    茶點故事閱讀 44,665評論 2 354