(七)App啟動(dòng)流程第2篇
書(shū)接上文,App啟動(dòng)一共有七個(gè)階段,上篇文章篇幅所限吻育,我們只看了第一階段,接下來(lái)講剩余的六個(gè)階段淤井,仍然是拿斗魚(yú)App舉例子布疼。
簡(jiǎn)單回顧一下第一階段的流程,就是Launcher向AMS發(fā)送一個(gè)跨進(jìn)程通信币狠,通過(guò)AMN/AMP游两,告訴AMS,我要啟動(dòng)斗魚(yú)App漩绵。
畫(huà)一個(gè)圖贱案,描述一下啟動(dòng)App所經(jīng)歷的7個(gè)階段:
第2階段 AMS處理Launcher傳過(guò)來(lái)的信息
這個(gè)階段主要是Binder的Server端在做事情。因?yàn)槲覀兪菦](méi)有機(jī)會(huì)修改Binder的Server端邏輯的止吐,所以這個(gè)階段看起來(lái)非潮ψ伲枯燥,我盡量說(shuō)的簡(jiǎn)單些碍扔。
1. 首先Binder瘩燥,也就是AMN/AMP,和AMS通信不同,肯定每次是做不同的事情颤芬,就比如說(shuō)這次Launcher要啟動(dòng)斗魚(yú)App,那么會(huì)發(fā)送類型為START_ACTIVITY——TRANSACTION的請(qǐng)求給AMS套鹅,同時(shí)會(huì)告訴AMS要啟動(dòng)哪個(gè)Activity站蝠。
?2. AMS說(shuō),好卓鹿,我知道了菱魔,然后它會(huì)干一件很有趣的事情,就是檢查斗魚(yú)App中的Manifest文件吟孙,是否存在要啟動(dòng)的Activity澜倦。如果不存在聚蝶,就拋出Activity not found的錯(cuò)誤,各位做App的同學(xué)對(duì)這個(gè)異常應(yīng)該再熟悉不過(guò)了藻治,經(jīng)常寫(xiě)了個(gè)Activity而忘記在Manifest中聲明了碘勉,就報(bào)這個(gè)錯(cuò),就是因?yàn)锳MS在這里做檢查桩卵。不管是新啟動(dòng)一個(gè)App的首頁(yè)验靡,還是在App內(nèi)部跳轉(zhuǎn)到另一個(gè)Activity,都會(huì)做這個(gè)檢查雏节。
?3. 但是Launcher還活著啊胜嗓,所以接下來(lái)AMS會(huì)通知Launcher,哥們兒沒(méi)你什么事了钩乍,你"停薪留職"吧辞州。那么AMS是通過(guò)什么途徑告訴Launcher的呢?
前面講過(guò)寥粹,Binder的雙方進(jìn)行通信是平等的变过,誰(shuí)發(fā)消息,誰(shuí)就是Client涝涤,接收的一方就是Server媚狰。Client這邊會(huì)調(diào)用Server的代理對(duì)象。
對(duì)于從Launcher發(fā)來(lái)的消息妄痪,通過(guò)AMS的代理對(duì)象AMP哈雏,發(fā)送給AMS。
那么當(dāng)AMS想給Launcher發(fā)消息衫生,又該怎么辦呢裳瘪?前面不是把Launcher以及它所在的進(jìn)程給傳過(guò)來(lái)了嗎?它在AMS這邊保存為一個(gè)ActivityRecord對(duì)象罪针,這個(gè)對(duì)象里面有一個(gè)ApplicationThreadProxy彭羹,單單從名字看就出賣了它,這就是一個(gè)Binder代理對(duì)象泪酱。它的Binder真身派殷,也就是ApplicationThread。
站在AIDL的角度墓阀,來(lái)畫(huà)這張圖毡惜,是這樣的:
所以結(jié)論是,AMS通過(guò)ApplicationThreadProxy發(fā)送消息斯撮,而App端則是通過(guò)ApplicationThread來(lái)接收這個(gè)消息经伙。
第3階段 Launcher去休眠,然后通知AMS勿锅,我真的已經(jīng)"停薪留職"了帕膜,沒(méi)有吃空餉
ApplicationThread(簡(jiǎn)稱APT)枣氧,它和ApplicationThreadProxy(簡(jiǎn)稱ATP)的關(guān)系,我們?cè)诘谌A段已經(jīng)介紹過(guò)了垮刹。
APT接收到來(lái)自AMS的消息后达吞,就調(diào)用ActivityThread的sendMessage方法,向Launcher的主線程消息隊(duì)列發(fā)送一個(gè)PAUSE_ACTIVITY消息荒典。
前面說(shuō)過(guò)酪劫,ActivityThread就是主線程(UI線程)
看到下面的代碼是不是很親切?
發(fā)送消息是通過(guò)一個(gè)名為H的Handler類的完成的种蝶,這個(gè)H類的名字真特么有個(gè)性契耿,想記不住都難瞒大。
做App的同學(xué)都知道螃征,繼承自Handler類的子類,就要實(shí)現(xiàn)handleMessage方法透敌,這里是一個(gè)switch…case語(yǔ)句盯滚,處理各種各樣的消息,PAUSE_ACTIVITY消息只是其中一種酗电,由此也能預(yù)見(jiàn)到魄藕,AMS給Activity發(fā)送的所有消息,以及給其它三大組件發(fā)送的所有消息撵术,都從H這里經(jīng)過(guò)背率,為什么要強(qiáng)調(diào)這一點(diǎn)呢,既然四大組件都走這條路嫩与,那么這里就可以做點(diǎn)手腳寝姿,從而做插件化技術(shù),這個(gè)我們以后介紹插件化技術(shù)的時(shí)候會(huì)講到划滋。
H對(duì)于PAUSE_ACTIVITY消息的處理饵筑,如上面的代碼,是調(diào)用ActivityThread的handlePauseActivity方法处坪。這個(gè)方法干兩件事:
- ActivityThread里面有一個(gè)mActivities集合根资,保存當(dāng)前App也就是Launcher中所有打開(kāi)的Activity,把它找出來(lái)同窘,讓它休眠玄帕。
- 通過(guò)AMP通知AMS,我真的休眠了想邦。
你可能會(huì)找不到H和APT這兩個(gè)類文件裤纹,那是因?yàn)樗鼈兌际茿ctivityThread的內(nèi)嵌類。
至此案狠,Launcher的工作完成了服傍。你可以看到在這個(gè)過(guò)程中钱雷,各個(gè)類都起到了什么作用。蕓蕓眾生吹零,粉墨登場(chǎng):
- APT
- ActivityThread
- H
第4階段 AMS啟動(dòng)新的進(jìn)程
接下來(lái)又輪到AMS做事了罩抗,你們會(huì)發(fā)現(xiàn)我不太喜歡講解AMS的流程,甚至都不畫(huà)UML圖灿椅,因?yàn)檫@部分邏輯和App開(kāi)發(fā)人員關(guān)系不是很大套蒂,我盡量說(shuō)的簡(jiǎn)單一些,把流程說(shuō)清楚就好茫蛹。
AMS接下來(lái)要啟動(dòng)斗魚(yú)App的首頁(yè)操刀,因?yàn)槎肤~(yú)App不在后臺(tái)進(jìn)程中,所以要啟動(dòng)一個(gè)新的進(jìn)程婴洼。這里調(diào)用的是Process.start方法骨坑,并且指定了ActivityThread的main函數(shù)為入口函數(shù)。
第5階段 新的進(jìn)程啟動(dòng)柬采,以ActivityThread的main函數(shù)作為入口
啟動(dòng)新進(jìn)程欢唾,其實(shí)就是啟動(dòng)一個(gè)新的App。
在啟動(dòng)新進(jìn)程的時(shí)候粉捻,為這個(gè)進(jìn)程創(chuàng)建ActivityThread對(duì)象礁遣,這就是我們耳熟能詳?shù)闹骶€程(UI線程)。
創(chuàng)建好UI線程后肩刃,立刻進(jìn)入ActivityThread的main函數(shù)祟霍,接下來(lái)要做2件具有重大意義的事情:
1)創(chuàng)建一個(gè)主線程Looper,也就是MainLooper盈包》心牛看見(jiàn)沒(méi),MainLooper就是在這里創(chuàng)建的续语。
2)創(chuàng)建Application垂谢。記住,Application是在這里生成的疮茄。
- - - - - - - - - - - - - -華麗的分割線 開(kāi)始- - - - - - - - - - - - - - -
App開(kāi)發(fā)人員對(duì)Application非常熟悉滥朱,因?yàn)槲覀兛梢栽谄渲袑?xiě)代碼,進(jìn)行一些全局的控制力试,所以我們通常認(rèn)為Application是掌控全局的徙邻,其實(shí)Application的地位在App中并沒(méi)有那么重要,它就是一個(gè)Context上下文畸裳,僅此而已缰犁。
App中的靈魂是ActivityThread,也就是主線程,只是這個(gè)類對(duì)于App開(kāi)發(fā)人員是訪問(wèn)不到的——使用反射倒是可以修改這個(gè)類的一些行為帅容。
- - - - - - - - - - - - - - -華麗的分割線 結(jié)束- - - - - - - - - - - - - - -
創(chuàng)建新App的最后颇象,就是告訴AMS,我啟動(dòng)好了并徘,同時(shí)把自己的ActivityThread對(duì)象發(fā)送給AMS遣钳,從此以后,AMS的電話簿中就多了這個(gè)新的App的登記信息麦乞,AMS以后向這個(gè)App發(fā)送消息蕴茴,就通過(guò)這個(gè)ActivityThread對(duì)象。
第6階段 AMS告訴新App啟動(dòng)哪個(gè)Activity
AMS把傳入的ActivityThread對(duì)象姐直,轉(zhuǎn)為一個(gè)ApplicationThread對(duì)象倦淀,用于以后和這個(gè)App跨進(jìn)程通信。還記得APT和ATP的關(guān)系嗎声畏?這就又回到第2階段的那張關(guān)系圖了撞叽。
還記得第1階段,Launcher發(fā)送給AMS要啟動(dòng)斗魚(yú)App的哪個(gè)Activity嗎砰识?這個(gè)信息被AMS存下來(lái)了能扒。
那么在第6階段佣渴,AMS從過(guò)去的記錄中翻出來(lái)要啟動(dòng)哪個(gè)Activity辫狼,然后通過(guò)ATP告訴App。
第7階段 啟動(dòng)斗魚(yú)首頁(yè)Activity
畢其功于一役辛润,盡在第7階段膨处。這是最后一步。
App砂竖,這個(gè)Binder的另一端真椿,通過(guò)APT接收到AMS的消息,仍然是在H的handleMessage方法的switch語(yǔ)句中處理乎澄,只不過(guò)突硝,這次消息的類型是LAUNCH_ACTIVITY:
ActivityClientRecord是什么?這是AMS傳遞過(guò)來(lái)的要啟動(dòng)的Activity置济。
還是這里解恰,我們仔細(xì)看那個(gè)getPackageInfoNoCheck方法,這個(gè)方法會(huì)提取Apk中的所有資源浙于,然后設(shè)置給r的packageInfo屬性护盈。這個(gè)屬性的類型很有名,叫做LoadedApk羞酗。各位記住這里腐宋,這個(gè)地方可以干壞事,也是插件化技術(shù)滲入的一個(gè)點(diǎn)。
在H的這個(gè)分支中胸竞,又反過(guò)來(lái)回調(diào)ActivityThread的handleLaunchActivity方法欺嗤,你要是覺(jué)得很繞那就對(duì)了。其實(shí)我一直覺(jué)得卫枝,ActivityThread和H合并成一個(gè)類也沒(méi)問(wèn)題剂府。
重新看一下這個(gè)過(guò)程,每次都是APT執(zhí)行ActivityThread的sendMessage方法剃盾,在這個(gè)方法中腺占,把消息拼裝一下,然后扔個(gè)H的swicth語(yǔ)句去分析痒谴,來(lái)決定要執(zhí)行ActivityThread的那個(gè)方法衰伯。每次都是這樣,習(xí)慣就好了积蔚。
handleLaunchActivity方法都做哪些事呢意鲸?
1)通過(guò)Instrumentation的newActivity方法,創(chuàng)建出來(lái)要啟動(dòng)的Activity實(shí)例尽爆。
2)為這個(gè)Activity創(chuàng)建一個(gè)上下文Context對(duì)象怎顾,并與Activity進(jìn)行關(guān)聯(lián)。
3)通過(guò)Instrumentation的callActivityOnCreate方法漱贱,執(zhí)行Activity的onCreate方法槐雾,從而啟動(dòng)Activity》ǎ看到這里是不是很熟悉很親切募强?
至此,App啟動(dòng)完畢崇摄。這個(gè)流程是經(jīng)過(guò)了很多次握手擎值, App和ASM,頻繁的向?qū)Ψ桨l(fā)送消息逐抑,而發(fā)送消息的機(jī)制鸠儿,是建立在Binder的基礎(chǔ)之上的。
下一篇文章厕氨,我們講Context家族进每。