startActivity 啟動過程分析課程

本屆課程主要講解了 startActivity 啟動過程源碼分析扼鞋,引用的源碼版本是 android-28申鱼。

假設(shè) ActivityA 跳轉(zhuǎn)到另一個App中的 ActivityB,過程如下圖所示:

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

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

ActivityA --> ActivityManagerService 階段

這一過程并不復(fù)雜捐友,用一張圖表示具體過程如下:

Activity 的 startActivity

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

Activity 的 startActivityForResult

startActivityForResult 也很簡單匣砖,調(diào)用 Instrumentation.execStartActivity 方法。剩下的交給 Instrumentation 類去處理昏滴。

解釋說明:

  • Instrumentation 類主要用來監(jiān)控應(yīng)用程序與系統(tǒng)交互猴鲫。
  • 藍(lán)框中的 mMainThread 是 ActivityThread 類型尺棋,ActivityThread 可以理解為一個進程键俱,在這就是 A 所在的進程。
  • 通過 mMainThread 獲取一個 ApplicationThread 的引用料饥,這個引用就是用來實現(xiàn)進程間通信的姻几,具體來說就是 AMS 所在系統(tǒng)進程通知應(yīng)用程序進程進行的一系列操作匣缘。

Instrumentation 的 execStartActivity

這里獲取了AMS,然后調(diào)用了startActivity方法鲜棠。實際上這里就是通過 AIDL 來調(diào)用 AMS 的 startActivity 方法,至此培慌,startActivity 的工作重心成功地從進程 A 轉(zhuǎn)移到了系統(tǒng)進程 AMS 中豁陆。

ActivityManagerService --> ApplicationThread

接下來就看下在 AMS 中是如何一步一步執(zhí)行到 B 進程的。

注:剛才在看 Instrumentation 的時候吵护,講過一個 ApplicationThread 類盒音,這個類是負(fù)責(zé)進程間通信的,這里 AMS 最終其實就是調(diào)用了 B 進程中的一個 ApplicationThread 引用馅而,從而間接地通知 B 進程進行相應(yīng)操作祥诽。

相比于 startActivity-->AMS,AMS-->ApplicationThread 流程看起來復(fù)雜好多了瓮恭,實際上這里面就干了 2 件事:

  1. 綜合處理 launchMode 和 Intent 中的 Flag 標(biāo)志位雄坪,并根據(jù)處理結(jié)果生成一個目標(biāo) Activity B 的對象(ActivityRecord)。
  2. 判斷是否需要為目標(biāo) Activity B 創(chuàng)建一個新的進程(ProcessRecord)屯蹦、新的任務(wù)棧(TaskRecord)维哈。

AMS 的 startActivity

經(jīng)過多個方法的調(diào)用绳姨,最終通過 obtainStarter 方法獲取了 ActivityStarter 類型的對象,然后調(diào)用其 execute 方法阔挠。在 execute 方法中飘庄,會再次調(diào)用其內(nèi)部的 startActivityMayWait 方法。

ActivityStarter 的 startActivityMayWait

ActivityStarter 這個類看名字就知道它專門負(fù)責(zé)一個 Activity 的啟動操作购撼。它的主要作用包括解析 Intent跪削、創(chuàng)建 ActivityRecord、如果有可能還要創(chuàng)建 TaskRecord迂求。startActivityMayWait 方法的部分實現(xiàn)如下:


這里的mSupervisor主要是負(fù)責(zé) Activity 所處棧的管理類碾盐。

在上圖中的 resolveIntent 中實際上是調(diào)用系統(tǒng) PackageManagerService 來獲取最佳 Activity。有時候我們通過隱式 Intent 啟動 Activity 時锁摔,系統(tǒng)中可能存在多個 Activity 可以處理 Intent廓旬,此時會彈出一個選擇框讓用戶選擇具體需要打開哪一個 Activity 界面,就是此處的邏輯處理結(jié)果谐腰。

在 startActivityMayWait 方法中調(diào)用了一個重載的 startActivity 方法孕豹,而最終會調(diào)用的 ActivityStarter 中的 startActivityUnchecked 方法來獲取啟動 Activity 的結(jié)果。

ActivityStarter 的 startActivityUnchecked

  • 圖中 1 處計算啟動 Activity 的 Flag 值励背。
  • 注釋 2 處處理 Task 和 Activity 的進棧操作。
  • 注釋 3 處啟動棧中頂部的 Activity叶眉。

computeLaunchingTaskFlags 方法具體如下:

這個方法的主要作用是計算啟動 Activity 的 Flag芹枷,不同的 Flag 決定了啟動 Activity 最終會被放置到哪一個 Task 集合中衅疙。

  • 圖中 1 處 mInTask 是 TaskRecord 類型,此處為 null鸳慈,代表 Activity 要加入的棧不存在,因此需要判斷是否需要新建 Task走芋。
  • 圖中 2 處的 mSourceRecord 的類型 ActivityRecord 類型,它是用來描述“初始 Activity”翁逞,什么是“初始 Activity”呢肋杖?比如 ActivityA 啟動了ActivityB,ActivityA 就是初始 Activity挖函。當(dāng)我們使用 Context 或者 Application 啟動 Activity 時,此 SourceRecord 為 null。
  • 圖中 3 處表示初始 Activity 如果是在 SingleInstance 棧中的 Activity浅萧,這種需要添加 NEW_TASK 的標(biāo)識。因為 SingleInstance 棧只能允許保存一個 Activity吩案。
  • 圖中 4 處表示如果 Launch Mode 設(shè)置了 singleTask 或 singleInstance,則也要創(chuàng)建一個新棧徘郭。

ActivityStackSupervisor 的 startActivityLocked

方法中會調(diào)用 insertTaskAtTop 方法嘗試將 Task 和 Activity 入棧丧肴。如果 Activity 是以 newTask 的模式啟動或者 TASK 堆棧中不存在該 Task id残揉,則 Task 會重新入棧芋浮,并且放在棧的頂部抱环。需要注意的是:Task 先入棧纸巷,之后才是 Activity 入棧镇草,它們是包含關(guān)系瘤旨。

這里一下子涌出了好幾個概念 Stack、Task存哲、Activity,其實它們都是在 AMS 內(nèi)部維護的數(shù)據(jù)結(jié)構(gòu)祟偷,可以用一張圖來描述它們之間的關(guān)系。


如:這次課程是 ActivityA 跳轉(zhuǎn)到另一個App中的 ActivityB杭棵,所以如果ActivityB所在的App沒有啟動。這里會先在AMS中的 ActivityStackSupervisor 中創(chuàng)建 ActivityStack,并且通過創(chuàng)建的 ActivityStack 來管理 TaskRecord先舷。Task會入棧,然后Activity入棧蒋川,不同啟動模式所放入的棧的方式也是不一樣的。如果是 singleTask 或 singleInstance 啟動模式缸浦,則會另外創(chuàng)建TaskRecord夕冲,并添加到 ActivityStack 進行管理裂逐。

ActivityStack 的 resumeFocusedStackTopActivityLocked

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

ActivityStackSupervisor 的 startSpecificActivityLocked

解釋說明:

  • 圖中 1 處根據(jù)進程名稱和 Application 的 uid 來判斷目標(biāo)進程是否已經(jīng)創(chuàng)建掺涛,如果沒有則代表進程未創(chuàng)建庭敦。
  • 圖中 2 處調(diào)用 AMS 創(chuàng)建 Activity 所在進程薪缆。創(chuàng)建進程也就進而創(chuàng)建了ActivityThread對象,AMS 最終也就是調(diào)用了 B 進程中的這個 ApplicationThread 引用拣帽。

不管是目標(biāo)進程已經(jīng)存在還是新建目標(biāo)進程,最終都會調(diào)用圖中紅線標(biāo)記的 realStartActivityLocked 方法來執(zhí)行啟動 Activity 的操作澜沟。

ActivityStackSupervisor 的 realStartActivityLocked

這個方法在 android-27 和 android-28 版本的區(qū)別很大,從 android-28 開始 Activity 的啟動交給了事務(wù)(Transaction)來完成茫虽。

  • 圖中 1 處創(chuàng)建 Activity 啟動事務(wù)既们,并傳入 app.thread 參數(shù)濒析,它是 ApplicationThread 類型啥纸。在上文 startActivity 階段已經(jīng)提過 ApplicationThread 是為了實現(xiàn)進程間通信的,是 ActivityThread 的一個內(nèi)部類斯棒。
  • 圖中 2 處執(zhí)行 Activity 啟動事務(wù)。

Activity 啟動事務(wù)的執(zhí)行是由 ClientLifecycleManager 來完成的荣暮,具體代碼如下:

可以看出實際上是調(diào)用了啟動事務(wù) ClientTransaction 的 schedule 方法,而這個 transaction 實際上是在創(chuàng)建 ClientTransaction 時傳入的 app.thread 對象护赊,也就是 ApplicationThread。如下所示:

解釋說明:

  • 這里傳入的 app.thread 會賦值給 ClientTransaction 的成員變量 mClient骏啰,ClientTransaction 會調(diào)用 mClient.scheduleTransaction(this) 來執(zhí)行事務(wù)。
  • 這個 app.thread 是 ActivityThread 的內(nèi)部類 ApplicationThread判耕,所以事務(wù)最終是調(diào)用 app.thread 的 scheduleTransaction 執(zhí)行。

到這為止 startActivity 操作就成功地從 AMS 轉(zhuǎn)移到了另一個進程 B 中的ApplicationThread中渺贤,剩下的就是 AMS 通過進程間通信機制通知 ApplicationThread 執(zhí)行 ActivityB 的生命周期方法。而這里的app.thread也是在startSpecificActivityLocked中創(chuàng)建進程時所創(chuàng)建的志鞍。

ApplicationThread -> Activity

在 ClientLifecycleManager 中會調(diào)用 ClientTransaction的schedule() 方法


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



可以看出,這里還是調(diào)用了ActivityThread 的 scheduleTransaction 方法委粉。但是這個方法實際上是在 ActivityThread 的父類 ClientTransactionHandler 中實現(xiàn),具體如下:

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

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

在 executeCallback 方法中忿墅,會遍歷事務(wù)中的 callback 并執(zhí)行 execute 方法

之前添加callback的地方

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

LaunchActivityItem 的 execute()

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

ActivityThread 的 handleLaunchActivity

這是一個比較重要的方法采转,Activity 的生命周期方法就是在這個方法中有序執(zhí)行瞬痘,具體如下:

解釋說明:

  • 圖中 1 處初始化 Activity 的 WindowManager故慈,每一個 Activity 都會對應(yīng)一個“窗口”框全,下一節(jié)會詳細(xì)講解。
  • 圖中 2 處調(diào)用 performLaunchActivity 創(chuàng)建并顯示 Activity津辩。
  • 圖中 3 處通過反射創(chuàng)建目標(biāo) Activity 對象。
  • 圖中 4 處調(diào)用 attach 方法建立 Activity 與 Context 之間的聯(lián)系喘沿,創(chuàng)建 PhoneWindow 對象,并與 Activity 進行關(guān)聯(lián)操作蚜印,下節(jié)會詳細(xì)講解。
  • 圖中 5 處通過 Instrumentation 最終調(diào)用 Activity 的 onCreate 方法窄赋。

至此,目標(biāo) Activity 已經(jīng)被成功創(chuàng)建并執(zhí)行生命周期方法寝凌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市红符,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌预侯,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萎馅,死亡現(xiàn)場離奇詭異虹蒋,居然都是意外死亡糜芳,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門塘辅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人扣墩,你說我怎么就攤上這事∩胩瑁” “怎么了滥比?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵亚脆,是天一觀的道長守呜。 經(jīng)常有香客問我,道長查乒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任玛迄,我火速辦了婚禮,結(jié)果婚禮上虏杰,老公的妹妹穿的比我還像新娘。我一直安慰自己纺阔,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布笛钝。 她就那樣靜靜地躺著愕宋,像睡著了一般玻靡。 火紅的嫁衣襯著肌膚如雪中贝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天邻寿,我揣著相機與錄音视哑,去河邊找鬼。 笑死黎炉,一個胖子當(dāng)著我的面吹牛醋拧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丹壕,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼薇溃,長吁一口氣:“原來是場噩夢啊……” “哼菌赖!你這毒婦竟也來了沐序?” 一聲冷哼從身側(cè)響起琉用,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤策幼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后特姐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡浅浮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年捷枯,在試婚紗的時候發(fā)現(xiàn)自己被綠了滚秩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淮捆。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖争剿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蚕苇,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布涩笤,位于F島的核電站盒件,受9級特大地震影響舱禽,放射性物質(zhì)發(fā)生泄漏炒刁。R本人自食惡果不足惜誊稚,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望里伯。 院中可真熱鬧,春花似錦疾瓮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肩碟。三九已至,卻和暖如春削祈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背岩瘦。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留叙凡,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓握爷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親新啼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,573評論 2 353