面試官:Activity的啟動流程异吻?還在AMS裹赴?那你就out了!api29源碼解析

Warning诀浪!本文基于API 29棋返,基于 API 30 的Activity啟動流程分析已更新,點擊:Activity啟動流程雷猪?基于Api30的Activity啟動流程分析 http://www.reibang.com/p/b68914037364

Activity的啟動流程api26

直接放圖睛竣。

第一次做這個圖的時候,我是按照api26的源碼來的春宣,然而現(xiàn)在已經(jīng)api29了……

Activity的啟動過程的源碼我看了多個版本的酵颁,每個版本都有一些變化,有些地方屬實搞得人摸不著頭腦月帝。尤其是中間這幾個類躏惋,一直在改動。比如這個ActivityStarter嚷辅,如果我沒記錯的話是api23還是26才加進(jìn)來的簿姨;后來又把ActivityStackSupervisor的一部分功能分離到了不同的類中◆じ悖看看源碼中的注釋:

ActivityStackSupervisor

好吧扁位,這個類即將被移除了……

個人感覺可能是Android團(tuán)隊前期埋的坑太多,留下了一些超巨型的類趁俊,很多類都超過了幾千上萬行域仇;然后隨著版本的更替,發(fā)現(xiàn)這么多功能都放一個類里面也不成寺擂,又在慢慢分離一些職責(zé)到其他類中暇务,所以就造成了一個版本一個樣泼掠,變化很快。代碼流程隨著版本優(yōu)化垦细,這也是必然的趨勢择镇。

不過呢,總體來講括改,大體的流程框架基本是固定的腻豌,太細(xì)節(jié)的地方也不需要過多糾結(jié)了。

言歸正傳嘱能,從頂至下吝梅,我個人把Activity的啟動流程依次分為三個階段
App進(jìn)程中 ——(通過Binder)—→ 系統(tǒng)進(jìn)程中 ——(通過Binder)—→ 回到App進(jìn)程中

下面依次進(jìn)行梳理。(只保留關(guān)鍵代碼)

1. App進(jìn)程中

App進(jìn)程第一輪做的事兒不多惹骂,主要就是把傳進(jìn)來的Intent扔給AMS憔涉。

1.1 Activity

在Activity中調(diào)用了startActivity方法后,不管調(diào)用的是哪個重載析苫,最后都會進(jìn)入到startActivityForResult(Intent, int, Bundle)中兜叨。

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
    ……
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
    ……
    }

然后就進(jìn)入到了Instrumentation.execStartActivity方法中。

1.2 Instrumentation

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
    ……
            ActivityTaskManager.getService().startActivity(
                        whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
    ……
    }

注意衩侥!重點国旷!
在之前的版本中,Instrumentation都是通過Binder的方式調(diào)用AMS的方法啟動Activity的茫死;但是在api29中跪但,IPC的對象變成了ActivityTaskManagerService(ATMS)。根據(jù)這個類的說明峦萎,“此類提供有關(guān)Activity及其容器(如task屡久,stack和display)的信息,并與之交互爱榔。”網(wǎng)上關(guān)于這個類的信息不多被环,但是通過源碼可以發(fā)現(xiàn),這個類實際上就是AMS分離出的一部分職責(zé)(畢竟AMS兩萬多行详幽,也該瘦瘦身了)筛欢。
Instrumentation.execStartActivity通過ActivityTaskManager.getService()獲得了一個IActivityTaskManager類型的對象,這是一個Binder對象唇聘,負(fù)責(zé)應(yīng)用和ATMS之間的通信版姑。也就是在這里,流程進(jìn)入到了第二階段:系統(tǒng)進(jìn)程中迟郎。

2. 系統(tǒng)進(jìn)程中

2.1 ATMS(ActivityTaskManagerService)

ATMS運行在系統(tǒng)服務(wù)進(jìn)程(system_server)之中剥险。當(dāng)App通過Instrumentation和ATMS跨進(jìn)程通信之后,ATMS就代管了接下來的啟動流程宪肖。
ATMS在進(jìn)行了簡單處理之后表制,就會交給ActivityStarter處理宇姚。(這其實跟之前的AMS一模一樣)

    public final int startActivity(...args) {
        return startActivityAsUser(...args);
    }

    public final int startActivityAsUser(args) {
        // ...
        return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
                .......
                .execute();
    }

上述代碼中,getActivityStartController().obtainStarter會返回一個ActivityStarter對象夫凸。(這個ActivityStartController中有一個ActivityStarter.DefaultFactory類型的成員變量,它維護(hù)了一個ActivityStarter池阱持。obtainStarter()就是從里面取的夭拌。
然后ActivityStarter.execute()會根據(jù)傳入的字符串"startActivityAsUser"來進(jìn)行接下來的流程。

2.2 一些重要的類

接下來的工作衷咽,就是一系列的類協(xié)同處理了鸽扁。代碼非常復(fù)雜,但是看個大概流程還是不難的镶骗,畢竟Android源代碼近億行桶现,不可能全部都仔細(xì)讀完。

這部分主要涉及了幾個類:ActivityStarter鼎姊、ActivityStackSupervisor骡和、ActivityRecordTaskRecord相寇、ActivityStack慰于。

ActivityStarter
顧名思義,ActivityStarter是用來啟動Activity的唤衫,同時也在其中判斷了啟動模式的邏輯婆赠;

ActivityStackSupervisor
顧名思義,ActivityStackSupervisor則是ActivityStack的管理者佳励。

ActivityRecord
而每次啟動一個Activity休里,都有一個對應(yīng)的ActivityRecord被記錄下來。這個ActivityRecord是在ActivityStarter.startActivity(IApplicationThread, ...)方法中被創(chuàng)建的赃承,也就是在這里妙黍,Activity的啟動信息完成了從intent到ActivityRecord的蛻變。ActivityRecord包含了一個Activity的所有信息瞧剖,可以看做是Activity的“身份證”废境。

TaskRecord
任務(wù)。一個TaskRecord就是在四種啟動模式中所說的“椡卜保”噩凹。一個TaskRecord,或者說一個Task毡咏、任務(wù)驮宴,根據(jù)官方說法,任務(wù)是用戶在執(zhí)行某項工作時與之互動的一系列 Activity 的集合呕缭。也就是說堵泽,一個任務(wù)是包含了若干個Activity的修己。對于啟動模式為singleInstance的Activity來說,會單獨存在于一個棧中迎罗,也就是這個任務(wù)中只有一個Activity睬愤;而其他啟動模式的Activity則會在當(dāng)前棧中進(jìn)行。具體的參考官網(wǎng)定義啟動模式纹安。

ActivityStack
ActivityStack顧名思義尤辱,也就是Activity棧。通常來講厢岂,一個系統(tǒng)里面有兩個ActivityStack光督,一個是系統(tǒng)Launcher所在的ActivityStack,一個是用戶App運行的ActivityStack塔粒。通常App所在的棧就是后者结借。
一個ActivityStack包含了多個TaskRecord,如圖所示:圖片來源

偷圖

2.3 ActivityStarter

顧名思義卒茬,ActivityStarter肩負(fù)著Activity啟動的職責(zé)船老。之前說到,ATMS調(diào)用了ActivityStarter.execute()方法圃酵,execute()又會調(diào)用startActivityMayWait()方法努隙。

startActivityMayWait
為什么這個方法名字里有個“MayWait”呢?因為接下來的流程都是在同步代碼塊里面進(jìn)行的辜昵,保證了Activity的啟動在多線程下的安全性荸镊。這個方法會做一系列的驗證和處理,比如根據(jù)Intent來從PackageManagerService收集待啟動Activity的信息(AndroidManifest中的信息)堪置。

startActivity
startActivityMayWait最終會調(diào)用startActivity躬存。startActivity共有三個重載,關(guān)鍵是在這個流程里面舀锨,這三個重載挨個都被調(diào)用了……不得不吐槽岭洲,Android這個方法名字取得真的毫無特色,不仔細(xì)看根本不知道他是干啥的坎匿;這三步我就合到一起寫了盾剩。過程很冗長,大多是做一些條件的判斷替蔬,以及繼續(xù)收集目標(biāo)Activity的信息告私。最重要的是,在這個過程中承桥,根據(jù)收集到的這些信息驻粟,目標(biāo)Activity對應(yīng)的ActivityRecord終于被創(chuàng)建了

startActivityUnchecked
經(jīng)過三個startActivity方法之后凶异,終于到了重頭戲了蜀撑。在startActivityUnchecked方法中挤巡,包含了關(guān)于啟動模式launchMode的邏輯。根據(jù)任務(wù)棧中是否已經(jīng)存在該Activity實例酷麦、AndroidManifest中設(shè)置的啟動模式和傳入Intent中的flags等矿卑,判斷出目標(biāo)Activity的啟動方式(《定義啟動模式》有詳盡的關(guān)于啟動模式的說明)。
在判斷完Activity的啟動方式之后沃饶,startActivityUnchecked方法中有這么一句代碼:

mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition, mOptions);

看到這個方法名母廷,我以為流程就走這繼續(xù)了呢;結(jié)果點進(jìn)去看了半天才發(fā)現(xiàn)绍坝,這個方法處理的是把目標(biāo)Activity壓入相應(yīng)的任務(wù)棧(或者新建任務(wù)棧或者pop上方Activity苔悦,諸如此類)相關(guān)的邏輯轩褐,另外就是創(chuàng)建Starting Window相關(guān)的邏輯;這算是個支線任務(wù)玖详。真正啟動Activity的流程還得往下看把介。
后面的邏輯就很清晰了,刪去旁支之后:

if (mDoResume) {
    // ...
    mRootActivityContainer.resumeFocusedStacksTopActivities(mTargetStack, mStartActivity, mOptions);
} 

不過這個mDoResume又是哪兒來的蟋座?經(jīng)過我的不懈努力拗踢,終于在某個startActivity方法中發(fā)現(xiàn)了,其實他就是true:

final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity);

OK向臀,到這就很明了了巢墅。所以說,到這又跟之前版本不同了券膀,沒有經(jīng)過ActivityStackSupervisor君纫,而是調(diào)用了RootActivityContainer.resumeFocusedStacksTopActivities

2.4 RootActivityContainer / ActivityStack / ActivityStackSupervisor

這個RootActivityContainer又是何方神圣呢芹彬?看看它的注釋:

Root node for activity containers. TODO: This class is mostly temporary to separate things out of ActivityStackSupervisor.java. The intention is to have this merged with RootWindowContainer.java as part of unifying the hierarchy.

意思就是蓄髓,這是個臨時類,分離了一部分ActivityStackSupervisor的職責(zé)舒帮,到最后這個類會被合并到RootWindowContainer類中会喝;之前這一步的確應(yīng)該是交給ActivityStackSupervisor的。根據(jù)源碼中的注釋所言玩郊,之后ActivityStackSupervisor類會被徹底消滅掉肢执,類的職責(zé)會被其他類分割。
總而言之译红,這個類暫時就是ActivityStackSupervisor一部分的替代品蔚万,之后這部分工作就應(yīng)該是RootWindowContainer來做了。

RootActivityContainer.resumeFocusedStacksTopActivities()及之后的一系列方法

回到RootActivityContainer.resumeFocusedStacksTopActivities方法中临庇;這開始之后的調(diào)用鏈依次是:
RootActivityContainer.resumeFocusedStacksTopActivities
-> ActivityStack.resumeTopActivityUncheckedLocked
-> ActivityStack.resumeTopActivityInnerLocked

它們的共同作用就是確保剛才壓入棧的目標(biāo)Activity能夠順利resume反璃,包括各種狀態(tài)的判斷昵慌、暫停當(dāng)前Activity、處理過渡動畫等淮蜈。而在ActivityStack.resumeTopActivityInnerLocked方法的最后:

if (next.attachedToProcess()) {
    // ...
} else {
    mStackSupervisor.startSpecificActivityLocked(next, true, true);
}

這個attachedToProcess方法的返回值也折騰了我很久斋攀;最后終于發(fā)現(xiàn),只有啟動了的Activity才會返回true梧田。所以代碼最終進(jìn)入到了ActivityStackSupervisor.startSpecificActivityLocked方法中淳蔼。此時第二階段的流程已經(jīng)接近尾聲。

ActivityStackSupervisor.startSpecificActivityLocked()中又調(diào)用了
ActivityStackSupervisor.realStartActivityLocked()
終于裁眯,看到這個名字就知道鹉梨,這是真正的啟動Activity了!

ActivityStackSupervisor.realStartActivityLocked(ActivityRecord, WindowProcessController, boolean, boolean)
這個方法有200多行穿稳,但是最關(guān)鍵的其實就是下面這一段:

    final ClientTransaction clientTransaction = ClientTransaction.obtain(
            proc.getThread(), r.appToken);
    clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
            System.identityHashCode(r), r.info,
            mergedConfiguration.getGlobalConfiguration(),
            mergedConfiguration.getOverrideConfiguration(), r.compat,
            r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
            r.icicle, r.persistentState, results, newIntents,
            dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
                    r.assistToken));
    mService.getLifecycleManager().scheduleTransaction(clientTransaction);

這里的mService是ATMS存皂,它通過LifecycleManager傳遞了一個Activity啟動的事務(wù);scheduleTransaction是這樣的:

    void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        final IApplicationThread client = transaction.getClient();
        transaction.schedule();
        // ...
    }

撥云見日逢艘!熟悉的IApplicationThread旦袋,終于結(jié)束了在系統(tǒng)進(jìn)程中的流程,又要回到App進(jìn)程了它改。

3. 重回App進(jìn)程

繼續(xù)偷圖疤孕,原文http://www.reibang.com/p/42cc38ba6112

https://upload-images.jianshu.io/upload_images/6635796-9f90882946ad4590.png

參考資料
http://www.reibang.com/p/42cc38ba6112
http://www.reibang.com/p/94816e52cd77
https://developer.android.google.cn/guide/components/activities/tasks-and-back-stack

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市央拖,隨后出現(xiàn)的幾起案子祭阀,更是在濱河造成了極大的恐慌,老刑警劉巖鲜戒,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柬讨,死亡現(xiàn)場離奇詭異,居然都是意外死亡袍啡,警方通過查閱死者的電腦和手機(jī)踩官,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來境输,“玉大人蔗牡,你說我怎么就攤上這事⌒崞剩” “怎么了辩越?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長信粮。 經(jīng)常有香客問我黔攒,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任督惰,我火速辦了婚禮不傅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赏胚。我一直安慰自己访娶,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布觉阅。 她就那樣靜靜地躺著崖疤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪典勇。 梳的紋絲不亂的頭發(fā)上劫哼,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機(jī)與錄音割笙,去河邊找鬼权烧。 笑死,一個胖子當(dāng)著我的面吹牛咳蔚,可吹牛的內(nèi)容都是我干的豪嚎。 我是一名探鬼主播搔驼,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼谈火,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了舌涨?” 一聲冷哼從身側(cè)響起糯耍,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎囊嘉,沒想到半個月后温技,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡扭粱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年舵鳞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片琢蛤。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡蜓堕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出博其,到底是詐尸還是另有隱情套才,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布慕淡,位于F島的核電站背伴,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜傻寂,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一息尺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧崎逃,春花似錦掷倔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至巴柿,卻和暖如春凛虽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背广恢。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工凯旋, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人钉迷。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓至非,卻偏偏與公主長得像,于是被迫代替她去往敵國和親糠聪。 傳聞我的和親對象是個殘疾皇子荒椭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354