文章轉(zhuǎn)自http://duanqz.github.io/2016-02-01-Activity-Maintenance
1. 概覽
Activity的管理有靜態(tài)和動(dòng)態(tài)兩層涵義:
靜態(tài)是指Activity的代碼組織結(jié)構(gòu),即Application中聲明的Activity的集合,這些Activity被組織在一個(gè)APK中,有特定的包名。 在編寫(xiě)應(yīng)用程序時(shí),Activity對(duì)應(yīng)到用戶(hù)界面,它定義了用戶(hù)界面的布局、交互行為、啟動(dòng)方式等,最重要的,是Activity的生命周期函數(shù)。 在應(yīng)用進(jìn)程看來(lái),只需要按照Android定義的規(guī)范,實(shí)現(xiàn)生命周期函數(shù)的具體邏輯即可,所有的用戶(hù)界面都遵循同一個(gè)規(guī)范。 編寫(xiě)完一個(gè)應(yīng)用程序的所有用戶(hù)界面,就算是完成了Activity的靜態(tài)管理。
動(dòng)態(tài)是指Activity的運(yùn)行調(diào)度方式,即Activity生命周期的執(zhí)行過(guò)程中,內(nèi)部數(shù)據(jù)結(jié)構(gòu)的變化,Android對(duì)所有Activity進(jìn)行統(tǒng)一管理。 在一個(gè)應(yīng)用程序安裝時(shí),系統(tǒng)會(huì)解析出APK中所有Activity的信息,當(dāng)顯示APK中的用戶(hù)界面時(shí),就需要調(diào)度Activity的生命周期函數(shù)了。 系統(tǒng)進(jìn)程(system_process)中維護(hù)了所有Activity的狀態(tài),管理中樞就是ActivityManagerService,Android為此做了精密的設(shè)計(jì),采用 棧 作為基本的數(shù)據(jù)結(jié)構(gòu)。
本文分析的Activity管理方式屬于動(dòng)態(tài)這個(gè)層面,也就是系統(tǒng)進(jìn)程中針對(duì)Activity的調(diào)度機(jī)制。
2. Activity管理的基礎(chǔ)
Activity的管理離不開(kāi)基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)以及它們之間的相互關(guān)聯(lián), 所以,筆者會(huì)從基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)出發(fā),分析類(lèi)的屬性和行為,并結(jié)合一些場(chǎng)景進(jìn)行源碼分析; 進(jìn)一步,會(huì)分析各個(gè)類(lèi)之間關(guān)聯(lián)關(guān)系的構(gòu)建過(guò)程。 這樣一來(lái),整個(gè)Activity管理運(yùn)轉(zhuǎn)的模型就清楚了,這個(gè)模型承載的很多業(yè)務(wù),本文不會(huì)具體展開(kāi), 在Android四大組件之Activity–啟動(dòng)過(guò)程一文中,筆者會(huì)再詳細(xì)介紹一種典型的業(yè)務(wù)。
2.1 數(shù)據(jù)結(jié)構(gòu)
Activity管理相關(guān)的數(shù)據(jù)結(jié)構(gòu)包括:
這些數(shù)據(jù)結(jié)構(gòu)都是Java類(lèi),它們都屬于系統(tǒng)進(jìn)程的范疇,即對(duì)象的構(gòu)建和銷(xiāo)毀都在系統(tǒng)進(jìn)程中完成,筆者將從類(lèi)的屬性和行為這兩個(gè)角度來(lái)分析類(lèi)的職能。 Android有一些約定俗成的函數(shù)命名方式,與Activity管理相關(guān)很多函數(shù)都會(huì)帶有Locked后綴,表示這些函數(shù)需要進(jìn)行多線(xiàn)程同步操作(synchronized),它們會(huì)讀/寫(xiě)一些多線(xiàn)程共享的數(shù)據(jù),讀者在分析代碼的時(shí)候可以適當(dāng)關(guān)注。
先上一張數(shù)據(jù)結(jié)構(gòu)的概覽圖:

圖中的方框可以理解為一個(gè)中包含關(guān)系:譬如一個(gè)TaskRecord中包含多個(gè)ActivityRecord; 圖中的連接線(xiàn)可以理解為等價(jià)關(guān)系,譬如同一個(gè)ActivityRecord會(huì)被TaskRecord和ProcessRecord引用,兩者是從不同維度來(lái)管理ActivityRecord。
- ActivityRecord是Activity管理的最小單位,它對(duì)應(yīng)著一個(gè)用戶(hù)界面;
- TaskRecord也是一個(gè)棧式管理結(jié)構(gòu),每一個(gè)TaskRecord都可能存在一個(gè)或多個(gè)ActivityRecord,棧頂?shù)?strong>ActivityRecord表示當(dāng)前可見(jiàn)的界面;
- ActivityStack是一個(gè)棧式管理結(jié)構(gòu),每一個(gè)ActivityStack都可能存在一個(gè)或多個(gè)TaskRecord,棧頂?shù)?strong>TaskRecord表示當(dāng)前可見(jiàn)的任務(wù);
- ActivityStackSupervisor管理著多個(gè)ActivityStack,但當(dāng)前只會(huì)有一個(gè)獲取焦點(diǎn)(Focused)的ActivityStack;
- ProcessRecord記錄著屬于一個(gè)進(jìn)程的所有ActivityRecord,運(yùn)行在不同TaskRecord中的ActivityRecord可能是屬于同一個(gè) ProcessRecord。
ActivityRecord
ActivityRecord是AMS調(diào)度Activity的基本單位,它需要記錄AndroidManifest.xml中所定義Activity的靜態(tài)特征,同時(shí), 也需要記錄Activity在被調(diào)度時(shí)的狀態(tài)變化,因此ActivityRecord這個(gè)類(lèi)的屬性比較多。
| 屬性 | 描述 |
|---|---|
| ActivityInfo | 從<activity>標(biāo)簽中解析出來(lái)的信息,包含launchMode,permission,taskAffinity等 |
| mActivityType | Activity的類(lèi)型有三種:APPLICATION_ACTIVITY_TYPE(應(yīng)用)、HOME_ACTIVITY_TYPE(桌面)、RECENTS_ACTIVITY_TYPE(最近使用) |
| appToken | 當(dāng)前ActivityRecord的標(biāo)識(shí) |
| packageName | 當(dāng)前所屬的包名,這是由<activity>靜態(tài)定義的 |
| processName | 當(dāng)前所屬的進(jìn)程名,大部分情況都是由<activity>靜態(tài)定義的,但也有例外 |
| taskAffinity | 相同taskAffinity的Activity會(huì)被分配到同一個(gè)任務(wù)棧中 |
| intent | 啟動(dòng)當(dāng)前Activity的Intent |
| launchedFromUid | 啟動(dòng)當(dāng)前Activity的UID,即發(fā)起者的UID |
| launchedFromPackage | 啟動(dòng)當(dāng)前Activity的包名,即發(fā)起者的包名 |
| resultTo | 在當(dāng)前ActivityRecord看來(lái),resultTo表示上一個(gè)啟動(dòng)它的ActivityRecord,當(dāng)需要啟動(dòng)另一個(gè)ActivityRecord,會(huì)把自己作為resultTo,傳遞給下一個(gè)ActivityRecord |
| state | ActivityRecord所處的狀態(tài),初始值是ActivityState.INITIALIZING |
| app | ActivityRecord的宿主進(jìn)程 |
| task | ActivityRecord的宿主任務(wù) |
| inHistory | 標(biāo)識(shí)當(dāng)前的ActivityRecord是否已經(jīng)置入任務(wù)棧中 |
| frontOfTask | 標(biāo)識(shí)當(dāng)前的ActivityRecord是否處于任務(wù)棧的根部,即是否為進(jìn)入任務(wù)棧的第一個(gè)ActivityRecord |
| newIntents | Intent數(shù)組,用于暫存還沒(méi)有調(diào)度到應(yīng)用進(jìn)程Activity的Intent |
由于ActivityRecord是一個(gè)最基本的數(shù)據(jù)結(jié)構(gòu),所以其行為相對(duì)較少,大都是一些用于判定和更新當(dāng)前ActivityRecord狀態(tài)的函數(shù):
| 行為 | 描述 |
|---|---|
| putInHistory(), takeFromHistory(), isInHistory() | 基于inHistory屬性,來(lái)判定和更新ActivityRecord是否在任務(wù)棧的狀態(tài)值 |
| isHomeActivity(), isRecentsActivity(), isApplicationActivity() | 基于mActivityType屬性,判定Activity的類(lèi)型 |
| setTask() | 設(shè)置ActivityRecord的宿主任務(wù) |
| deliverNewIntentLocked() | 向當(dāng)前ActivityRecord繼續(xù)派發(fā)Intent。在一些場(chǎng)景下,位于任務(wù)棧頂?shù)腁ctivityRecord會(huì)繼續(xù)接受新的Intent(譬如以singleTop方式啟動(dòng)的同一個(gè)Activity),這時(shí)候,會(huì)觸發(fā)調(diào)度Activity.onNewIntent()函數(shù) |
| addNewIntentLocked() | 如果Intent沒(méi)有派發(fā)到應(yīng)用進(jìn)程,則通過(guò)該函數(shù)往newIntents數(shù)組中添加一個(gè)元素。 |
要理解ActivityRecord這個(gè)數(shù)據(jù)結(jié)構(gòu),可以從其構(gòu)造函數(shù)出發(fā),理解其屬性在什么場(chǎng)景下會(huì)發(fā)生變化。 每次需要啟動(dòng)一個(gè)新的Activity時(shí),都會(huì)構(gòu)建一個(gè)ActivityRecord對(duì)象,這在ActivityStackSupervisor.startActivityLocked()函數(shù)中完成,構(gòu)造一個(gè)ActivityRecord要傳入的參數(shù)是相當(dāng)多的:
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration,
ActivityRecord _resultTo, String _resultWho, int _reqCode,
boolean _componentSpecified, ActivityStackSupervisor supervisor,
ActivityContainer container, Bundle options) {
service = _service; // AMS對(duì)象
appToken = new Token(this); //appToken可以進(jìn)行跨進(jìn)程傳遞,標(biāo)識(shí)一個(gè)AR對(duì)象
info = aInfo; //從AndroidManifest.xml中解析得到的Activity信息
launchedFromUid = _launchedFromUid; //譬如從瀏覽器啟動(dòng)當(dāng)前AR,那么該屬性記錄的就是瀏覽器的UID
launchedFromPackage = _launchedFromPackage;
userId = UserHandle.getUserId(aInfo.applicationInfo.uid);
intent = _intent; //啟動(dòng)當(dāng)前AR的Intent
shortComponentName = _intent.getComponent().flattenToShortString();
resolvedType = _resolvedType;
componentSpecified = _componentSpecified;
configuration = _configuration;
resultTo = _resultTo; //記錄上一個(gè)AR對(duì)象
resultWho = _resultWho; //reslutTo的字符串標(biāo)識(shí)
requestCode = _reqCode; //上一個(gè)AR對(duì)象設(shè)定的Request Code
state = ActivityState.INITIALIZING; //AR的狀態(tài),Activity調(diào)度時(shí)發(fā)生改變
frontOfTask = false; //是否處于Task的根部,調(diào)整任務(wù)棧中AR順序時(shí),可能發(fā)生改變
launchFailed = false;
stopped = false; //pause操作完成狀態(tài)位
delayedResume = false;
finishing = false; //stoped到finished之間的過(guò)渡狀態(tài)位
configDestroy = false;
keysPaused = false; //如果置為true,則當(dāng)前AR不再接受用戶(hù)輸入
inHistory = false; //將AR壓入任務(wù)棧后,該狀態(tài)位被置為true
visible = true;
waitingVisible = false;
nowVisible = false;
idle = false;
hasBeenLaunched = false;
mStackSupervisor = supervisor;
mInitialActivityContainer = container;
if (options != null) {
pendingOptions = new ActivityOptions(options);
mLaunchTaskBehind = pendingOptions.getLaunchTaskBehind();
}
haveState = true;
if (aInfo != null) {
//根據(jù)aInfo給realActivity, taskAffinity, processName等屬性賦值
...
} else {
//沒(méi)有aInfo的情況下,賦予默認(rèn)值
realActivity = null;
taskAffinity = null;
stateNotNeeded = false;
appInfo = null;
processName = null;
packageName = null;
fullscreen = true;
noDisplay = false;
mActivityType = APPLICATION_ACTIVITY_TYPE;
immersive = false;
}
}
</figure>
TaskRecord
TaskRecord的職責(zé)是管理多個(gè)ActivityRecord,本文所述的任務(wù)、任務(wù)棧指的就是TaskRecord。 啟動(dòng)Activity時(shí),需要找到Activity的宿主任務(wù),如果不存在,則需要新建一個(gè),也就是說(shuō)所有的ActivityRecord都必須有宿主。 TaskRecord與ActivityRecord是一對(duì)多的關(guān)系,TaskRecord的屬性中包含了ActivityRecord的數(shù)組; 同時(shí),TaskRecord還需要維護(hù)任務(wù)棧本身的狀態(tài)。
| 屬性 | 描述 |
|---|---|
| taskid | TaskRecord的唯一標(biāo)識(shí) |
| taskType | 任務(wù)棧的類(lèi)型,等同于ActivityRecord的類(lèi)型,是由任務(wù)棧的第一個(gè)ActivityRecord決定的 |
| intent | 在當(dāng)前任務(wù)棧中啟動(dòng)的第一個(gè)Activity的Intent將會(huì)被記錄下來(lái),后續(xù)如果有相同的Intent時(shí),會(huì)與已有任務(wù)棧的Intent進(jìn)行匹配,如果匹配上了,就不需要再新建一個(gè)TaskRecord了 |
| realActivity, origActivity | 啟動(dòng)任務(wù)棧的Activity,這兩個(gè)屬性是用包名(CompentName)表示的,real和orig是為了區(qū)分Activity有無(wú)別名(alias)的情況,如果AndroidManifest.xml中定義的Activity是一個(gè)alias,則此處real表示Activity的別名,orig表示真實(shí)的Activity |
| affinity | TaskRecord把Activity的affinity記錄下來(lái),后續(xù)啟動(dòng)Activity時(shí),會(huì)從已有的任務(wù)棧中匹配affinity,如果匹配上了,則不需要新建TaskRecord |
| rootAffinity | 記錄任務(wù)棧中最底部Activity的affinity,一經(jīng)設(shè)定后就不再改變 |
| mActivities | 這是TaskRecord最重要的一個(gè)屬性,TaskRecord是一個(gè)棧結(jié)構(gòu),棧的元素是ActivityRecord,其內(nèi)部實(shí)現(xiàn)是一個(gè)數(shù)組mActivities |
| stack | 當(dāng)前TaskRecord所在的ActivityStack |
TaskRecord的行為側(cè)重在TaskRecord本身的管理:增/刪/改/查任務(wù)棧中的元素。
| 行為 | 描述 |
|---|---|
| getRootActivity(), getTopActivity() | 任務(wù)棧有根部(Root)和頂部(Top),可以通過(guò)這兩個(gè)函數(shù)分別獲取到根部和頂部的ActivityRecord。獲取的過(guò)程就是對(duì)TaskRecord.mActivities進(jìn)行遍歷,如果ActivityRecord的狀態(tài)不是finishing,就認(rèn)為是有效的ActivityRecord |
| topRunningActivityLocked() | 雖然也是從頂至底對(duì)任務(wù)棧進(jìn)行遍歷獲取頂部的ActivityRecord,但這個(gè)函數(shù)同getTopActivity()有區(qū)別:輸入?yún)?shù)notTop,表示在遍歷的過(guò)程中需要排除notTop這個(gè)ActivityRecord; |
| addActivityToTop(), addActivityAtBottom() | 將ActivityRecord添加到任務(wù)棧的頂部或底部 |
| moveActivityToFrontLocked() | 該函數(shù)將一個(gè)ActivityRecord移至TaskRecord的頂部,實(shí)現(xiàn)方法就是先刪除已有的,再在棧頂添加一個(gè)新的 |
| setFrontOfTask() | ActivityRecord有一個(gè)屬性是frontOfTask,表示ActivityRecord是否為T(mén)askRecord的根Activity。該函數(shù)設(shè)置TaskRecord中所有ActivityRecord的frontOfTask屬性,從棧底往上開(kāi)始遍歷,第一個(gè)不處于finishing狀態(tài)的ActivityRecord的frontOfTask屬性置成true,其他都為false |
| performClearTaskLocked() | 清除TaskRecord中的ActivityRecord。當(dāng)啟動(dòng)Activity時(shí),用了Intent.FLAG_ACTIVITY_CLEAR_TOP參數(shù),那么在宿主任務(wù)中,待啟動(dòng)ActivityRecord之上的其他ActivityRecord都會(huì)被清除 |
僅僅把類(lèi)的屬性和行為羅列出來(lái),當(dāng)然不足以理解TaskRecord的工作原理。 接下來(lái),將深入部分函數(shù)的代碼,分析TaskRecord在一些場(chǎng)景下的具體執(zhí)行邏輯。
場(chǎng)景 1 啟動(dòng)一個(gè)Activity時(shí),通常需要將ActivityRecord壓入任務(wù)棧頂,addActivityToTop()就是為此功能設(shè)計(jì):
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
void addActivityToTop(ActivityRecord r) {
addActivityAtIndex(mActivities.size(), r);
}
</figure>
將ActivityRecord壓入棧頂,其實(shí)就是在mActivities數(shù)組末尾添加一個(gè)元素,所以,實(shí)際壓入棧頂?shù)牟僮魇怯?strong>addActivityAtIndex()完成:
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
void addActivityAtIndex(int index, ActivityRecord r) {
// 1\. 移除已有的ActivityRecord對(duì)象
if (!mActivities.remove(r) && r.fullscreen) {
numFullscreen++;
}
// 2\. 根據(jù)任務(wù)棧是否為空,設(shè)置狀態(tài)
if (mActivities.isEmpty()) {
taskType = r.mActivityType;
isPersistable = r.isPersistable();
mCallingUid = r.launchedFromUid;
mCallingPackage = r.launchedFromPackage;
maxRecents = Math.min(Math.max(r.info.maxRecents, 1),
ActivityManager.getMaxAppRecentsLimitStatic());
} else {
r.mActivityType = taskType;
}
// 3\. 在指定的位置添加ActivityRecord
mActivities.add(index, r);
// 4\. 更新任務(wù)棧關(guān)聯(lián)的Intent
updateEffectiveIntent();
...
}
</figure>
該函數(shù)會(huì)經(jīng)過(guò)以下處理過(guò)程:
移除任務(wù)棧中已有的ActivityRecord對(duì)象,即任務(wù)棧中不會(huì)出現(xiàn)兩個(gè)同樣的ActivityRecord對(duì)象。此處需要注意,兩次啟動(dòng)同一個(gè)Activity,是會(huì)產(chǎn)生兩個(gè)不同的ActivityRecord對(duì)象的;
如果任務(wù)棧為空,則設(shè)置任務(wù)棧的初始狀態(tài),否則,設(shè)置ActivityRecord的類(lèi)型為任務(wù)棧的類(lèi)型。由此可見(jiàn),同一個(gè)任務(wù)棧中,所有ActivityRecord的類(lèi)型都是一樣的,而且是由任務(wù)棧的第一個(gè)ActivityRecord的類(lèi)型決定的;
此處的位置就是任務(wù)棧頂,也就是mActivities屬性的末尾;
任務(wù)棧中元素發(fā)生了變化,所以需要更新任務(wù)棧關(guān)聯(lián)的Intent,這是通過(guò)調(diào)用updateEffectiveIntent()實(shí)現(xiàn)的,函數(shù)的具體邏輯,在
場(chǎng)景 3中再行分析。
場(chǎng)景 2 當(dāng)待顯示的Activity壓入任務(wù)棧后,就需要設(shè)置棧頂ActivityRecord的狀態(tài),這時(shí)候,會(huì)調(diào)用topRunningActivityLocked()函數(shù)來(lái)獲取棧頂?shù)脑兀瑸榱烁玫姆治?strong>topRunningActivityLocked()的使用場(chǎng)景,筆者把另一個(gè)與其功能相似的函數(shù)getTopActivity()也列出來(lái):
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
ActivityRecord getTopActivity() {
for (int i = mActivities.size() - 1; i >= 0; --i) {
final ActivityRecord r = mActivities.get(i);
if (r.finishing) {
continue;
}
return r;
}
return null;
}
ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = mActivities.get(activityNdx);
// 除了要求ActivityRecord不是finishing狀態(tài)以外,還要求不是當(dāng)前給定輸入的ActivityRecord
if (!r.finishing && r != notTop && stack.okToShowLocked(r)) {
return r;
}
}
}
</figure>
兩者是從頂?shù)降讓?duì)任務(wù)棧進(jìn)行遍歷,但實(shí)現(xiàn)邏輯不同,topRunningActivityLocked()接受一個(gè)輸入?yún)?shù)notTop,在尋找時(shí),要求排除notTop指定的ActivityRecord,通常,傳入的notTop都是null,隱含的意思就是棧頂?shù)腁ctivityRecord還沒(méi)有被銷(xiāo)毀。從函數(shù)命名topRunning,也可以看出其與getTop的區(qū)別:getTop不管棧頂?shù)乃阑睿玫骄秃? topRunning要求拿到的一定是活的棧頂。
另外,topRunningActivityLocked()還有一個(gè)限制條件: ActivityRecord是可以被顯示的(okToShow),這是通過(guò)ActivityStack.okToShowLocked()來(lái)判定的,主要是為了應(yīng)多多用戶(hù)或鎖屏顯示的Activity,一般情況下,函數(shù)返回值都為true。
場(chǎng)景 3 假定在啟動(dòng)一個(gè)Activity時(shí),設(shè)置了Intent的FLAG_ACTIVITY_REORDER_TO_FRONT,表示要將Activity重排序到任務(wù)棧頂。 如果目標(biāo)的Activity在任務(wù)棧中已經(jīng)啟動(dòng)過(guò),則需要將其挪至棧頂。譬如目標(biāo)任務(wù)棧從底到頂是 A - B - C, 然后,又以FLAG_ACTIVITY_REORDER_TO_FRONT啟動(dòng)了 A,那最終任務(wù)棧會(huì)變化為 B - C - A 。 這就會(huì)調(diào)用到moveActivityToFrontLocked()函數(shù):
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
final void moveActivityToFrontLocked(ActivityRecord newTop) {
mActivities.remove(newTop);
mActivities.add(newTop);
updateEffectiveIntent();
setFrontOfTask();
}
</figure>
該函數(shù)需要調(diào)整任務(wù)棧中ActivityRecord的順序,延用上述例子, A 將作為參數(shù)newTop。 首先,會(huì)將 A 從任務(wù)棧中移除; 然后,再將 A 添加到任務(wù)棧頂; 接著,會(huì)調(diào)用updateEffectiveIntent()函數(shù)來(lái)更新任務(wù)棧關(guān)聯(lián)的Intent:
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
void updateEffectiveIntent() {
final int effectiveRootIndex = findEffectiveRootIndex();
final ActivityRecord r = mActivities.get(effectiveRootIndex);
setIntent(r);
}
</figure>
該函數(shù)會(huì)找到一個(gè)有效的Root Index,即任務(wù)棧底部的索引,根據(jù)這個(gè)索引值取出對(duì)應(yīng)的ActivityRecord。 延續(xù)上述例子,B會(huì)被調(diào)整為任務(wù)棧的根部ActivityRecord,通過(guò)調(diào)用setIntent()來(lái)設(shè)置當(dāng)前任務(wù)棧關(guān)聯(lián)的Intent為啟動(dòng) B 的Intent,然而,這里可不止改變TaskRecord.intent這一個(gè)屬性這個(gè)簡(jiǎn)單,與Taskrecord的發(fā)起者相關(guān)的屬性值都要更改, 譬如mCallingUid,mCallingPackage都得更改為 B 的發(fā)起者:
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
void setIntent(ActivityRecord r) {
setIntent(r.intent, r.info);
mCallingUid = r.launchedFromUid;
mCallingPackage = r.launchedFromPackage;
}
</figure>
這里還有一個(gè)重載的setIntent()函數(shù),不再展開(kāi)分析了,只需要知道諸如affinity, realActivity等屬性都會(huì)被重置即可。
更新完TaskRecord的Intent,再回到moveActivityToFrontLocked()函數(shù)中,需要繼續(xù)更新任務(wù)棧的Front。 之前任務(wù)棧的Front是 A,在發(fā)生變化后, Front需要更新為 B,然而,TaskRecord并沒(méi)有一個(gè)屬性用來(lái)記錄當(dāng)前的Front, 它是根據(jù)任務(wù)棧中每一個(gè)ActivityRecord的frontOfTask屬性來(lái)判定的:
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
final void setFrontOfTask() {
boolean foundFront = false;
final int numActivities = mActivities.size();
for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
// 從棧底往上遍歷
final ActivityRecord r = mActivities.get(activityNdx);
if (foundFront || r.finishing) {
// 其他ActivityRecord的這個(gè)屬性都置為false
r.frontOfTask = false;
} else {
// 不為finishing狀態(tài),表示已經(jīng)找到了front的ActivityRecord
r.frontOfTask = true;
foundFront = true;
}
if (!foundFront && numActivities > 0) {
mActivities.get(0).frontOfTask = true;
}
}
</figure>
該函數(shù)從底到頂對(duì)任務(wù)棧進(jìn)行遍歷,找到的第一個(gè)未結(jié)束(finishing = faulse)的ActivityRecord, 將其frontOfTask屬性設(shè)置成true;其他所有ActivtyRecord的frontOfTask屬性設(shè)置為false。
ActivityStack
ActivityStack的職責(zé)是管理多個(gè)任務(wù)棧(TaskRecord),它是一個(gè)棧式結(jié)構(gòu),棧中的元素是TaskRecord。 每個(gè)Activity在特定的時(shí)刻都會(huì)有一個(gè)狀態(tài),譬如顯示、銷(xiāo)毀等, 在應(yīng)用進(jìn)程看來(lái),這些狀態(tài)的變化就是在執(zhí)行Activity的生命周期函數(shù); 在系統(tǒng)進(jìn)程看來(lái),這些狀態(tài)的變化都需要經(jīng)過(guò)ActivityStack來(lái)驅(qū)動(dòng)。 Activity的狀態(tài)是通過(guò)ActivityState這個(gè)枚舉類(lèi)來(lái)定義的:
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
enum ActivityState {
INITIALIZING,
RESUMED,
PAUSING,
PAUSED,
STOPPING,
STOPPED,
FINISHING,
DESTROYING,
DESTROYED
}
</figure>
從INITIALIZING到DESTROYED,所定義狀態(tài)值示意了Activity生命周期的走向。
| 屬性 | 描述 |
|---|---|
| stackId | 每一個(gè)ActivityStack都有一個(gè)編號(hào),從0開(kāi)始遞增。編號(hào)為0,表示桌面(Launcher)所在的ActivityStack,叫做Home Stack |
| mTaskHistory | TaskRecord數(shù)組,ActivityStack棧就是通過(guò)這個(gè)數(shù)組實(shí)現(xiàn)的 |
| mPausingActivity | 在發(fā)生Activity切換時(shí),正處于Pausing狀態(tài)的Activity |
| mResumedActivity | 當(dāng)前處于Resumed狀態(tài)的ActivityRecord |
| mStacks | ActivityStack會(huì)綁定到一個(gè)顯示設(shè)備上,譬如手機(jī)屏幕、投影儀等,在AMS中,通過(guò)ActivityDisplay這個(gè)類(lèi)來(lái)抽象表示一個(gè)顯示設(shè)備,ActivityDisplay.mStacks表示當(dāng)前已經(jīng)綁定到顯示設(shè)備的所有ActivityStack。當(dāng)執(zhí)行一次綁定操作時(shí),就會(huì)將ActivityStack.mStacks這個(gè)屬性賦值成ActivityDisplay.mStacks,否則,ActivityStack.mStacks就為null。簡(jiǎn)而言之,當(dāng)mStacks不為null時(shí),表示當(dāng)前ActivityStack已經(jīng)綁定到了一個(gè)顯示設(shè)備 |
Activity狀態(tài)的變遷,不僅僅是給ActivityRecord.state賦一個(gè)狀態(tài)值那么簡(jiǎn)單,ActivityStack要對(duì)棧進(jìn)行調(diào)整:之前的Activity要銷(xiāo)毀或者挪到后臺(tái),待顯示的Activity要挪到棧頂,這一調(diào)整,涉及到的工作就多了。
| 行為 | 描述 |
|---|---|
| findTaskLocked() | 該函數(shù)的功能是找到目標(biāo)ActivityRecord(target)所在的任務(wù)棧(TaskRecord),如果找到,則返回棧頂?shù)腁ctivityRecord,否則,返回null |
| findActivityLocked() | 根據(jù)Intent和ActivityInfo這兩個(gè)參數(shù)可以獲取一個(gè)Activity的包名,該函數(shù)會(huì)從棧頂至棧底遍歷ActivityStack中的所有Activity,如果包名匹配成功,就返回 |
| moveToFront) | 該函數(shù)用于將當(dāng)前的ActivityStack挪到前臺(tái),執(zhí)行時(shí),調(diào)用ActivityStack中的其他一些判定函數(shù) |
| isAttached() | 用于判定當(dāng)前ActivityStack是否已經(jīng)綁定到顯示設(shè)備 |
| isOnHomeDisplay() | 用于判定當(dāng)前是否為默認(rèn)的顯示設(shè)備(Display.DEFAULT_DISPLAY),通常,默認(rèn)的顯示設(shè)備就是手機(jī)屏幕 |
| isHomeStack() | 用于判定當(dāng)前ActivityStack是否為Home Stack,即判定當(dāng)前顯示的是否為桌面(Launcher) |
| moveTaskToFrontLocked() | 該函數(shù)用于將指定的任務(wù)棧挪到當(dāng)前ActivityStack的最前面。在Activity狀態(tài)變化時(shí),需要對(duì)已有的ActivityStack中的任務(wù)棧進(jìn)行調(diào)整,待顯示Activity的宿主任務(wù)需要挪到前臺(tái) |
| insertTaskAtTop() | 將任務(wù)插入ActivityStack棧頂 |
ActivityStack還有很多與遷移Activity狀態(tài)相關(guān)的行為: startActivityLocked(), resumeTopActivityLocked(), completeResumeLocked(), startPausingLocked(), completePauseLocked(), stopActivityLocked(), activityPausedLocked(), finishActivityLocked(), activityDestroyedLocked(), 它們與Activity的生命周期調(diào)度息息相關(guān),在Android四大組件之Activity–啟動(dòng)過(guò)程一文中,會(huì)再詳細(xì)分析這幾個(gè)函數(shù)的實(shí)現(xiàn)邏輯,本文還是通過(guò)一個(gè)簡(jiǎn)單的場(chǎng)景來(lái)分析ActivityStack的行為。
場(chǎng)景 1 以singleTask的方式啟動(dòng)一個(gè)處于后臺(tái)的Activity,那么,就需要將Activity挪到前臺(tái)。怎么挪呢?
第一步,findTaskLocked(): 找到Activity所在TaskRecord;
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
ActivityRecord findTaskLocked(ActivityRecord target) {
...
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
...
final ActivityRecord r = task.getTopActivity();
...
final Intent taskIntent = task.intent;
final Intent affinityIntent = task.affinityIntent;
...
if (!isDocument && !taskIsDocument && task.rootAffinity != null) {
if (task.rootAffinity.equals(target.taskAffinity)) {
return r;
}
} else if (taskIntent != null && taskIntent.getComponent() != null &&
taskIntent.getComponent().compareTo(cls) == 0 &&
Objects.equals(documentData, taskDocumentData)) {
return r;
} else if if (affinityIntent != null && affinityIntent.getComponent() != null &&
affinityIntent.getComponent().compareTo(cls) == 0 &&
Objects.equals(documentData, taskDocumentData)) {
return r
}
...
}
return null;
}
</figure>
該函數(shù)的功能是找到target ActivityRecord所在的Task,如果找到,則返回Task棧頂?shù)腁ctivityRecord,否則,返回null。 主體邏輯是對(duì)ActivityStack中的所有Task進(jìn)行遍歷,以下幾種情況表示找到了ActivityRecord的宿主task:
- Affinity相同。rootAffinity表示第一次啟動(dòng)該task時(shí)affinity值,如果一個(gè)ActivityRecord的taskAffinity屬性與其相等, 那么這個(gè)task自然是ActivityRecord的宿主;
- Intent的包名相同。
- Affinity Intent的包名相同。
第二步,moveToFront(): 將TaskRecord所在的ActivityStack挪到前臺(tái);
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
final void moveToFront(String reason) {
if (isAttached()) {
if (isOnHomeDisplay()) {
mStackSupervisor.moveHomeStack(isHomeStack(), reason);
}
mStacks.remove(this);
mStacks.add(this);
final TaskRecord task = topTask();
if (task != null) {
mWindowManager.moveTaskToTop(task.taskId);
}
}
}
</figure>
首先,會(huì)有一些判定:
- isAttached(): 只有在當(dāng)前ActivityStack綁定到顯示設(shè)備的情況下,才需要挪到;
- isOnHomeDisplay(): 如果當(dāng)前是默認(rèn)的顯示設(shè)備,則對(duì)HomeStack(桌面)進(jìn)行挪動(dòng), 這涉及到對(duì)多個(gè)ActivityStack的操作,所以需要通過(guò)ActivityStackSupervisor完成;
- isHomeStack(): 如果當(dāng)前是HomeStack,則將其挪到前后; 否則,將HomeStack挪到后臺(tái)
然后,就是對(duì)mStacks這個(gè)屬性進(jìn)行操作:在mStacks數(shù)組中,刪除已有的ActivityStack對(duì)象,并添加一個(gè)新的,這樣做其實(shí)達(dá)到了一個(gè)目的,前臺(tái)的ActivityStacks處于mStacks末尾。
最后,調(diào)用WMS.moveTaskToTop()通知窗口的進(jìn)行變化調(diào)整。
第三步, moveTaskToFrontLocked(): 將TaskRecord挪到ActivityStack的棧頂;
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord source, Bundle options,
String reason) {
final int numTasks = mTaskHistory.size();
final int index = mTaskHistory.indexOf(tr);
// 判定ActivityStack是否需要挪動(dòng)任務(wù)棧
if (numTasks == 0 || index < 0) {
...
return;
}
// 調(diào)整ActivityStack
insertTaskAtTop(tr);
moveToFront(reason);
...
// 將棧頂?shù)腁ctivity置為Resumed狀態(tài)
mStackSupervisor.resumeTopActivitiesLocked();
}
</figure>
首先,會(huì)經(jīng)過(guò)判定:如果當(dāng)前的ActivityStack為空,或者不存在要挪動(dòng)的任務(wù),則不需要對(duì)當(dāng)前ActivityStack進(jìn)行調(diào)整;
然后,確定目標(biāo)任務(wù)在當(dāng)前ActivityStack中,則對(duì)ActivityStack進(jìn)行調(diào)整,將目標(biāo)任務(wù)插入ActivityStack棧頂。
- insertTaskAtTop(),會(huì)先將已有的目標(biāo)任務(wù)刪除,再重新添加到棧頂位置;
- moveToFront(),在第二步中執(zhí)行過(guò)一次,因?yàn)樵谀承﹫?chǎng)景下,只會(huì)調(diào)用moveToFront(),不會(huì)調(diào)用moveTaskToFrontLocked(); 一旦要將任務(wù)挪到ActivityStack棧頂,意味著ActivityStack也一定要挪到前臺(tái);
最后,將任務(wù)棧頂?shù)腁ctivity置為Resumed狀態(tài),這里是通過(guò)ActivityStackSupervisor完成的。因?yàn)榭赡芡瑫r(shí)存在多個(gè)顯示設(shè)備,所以需要對(duì)多個(gè)ActivityStack進(jìn)行操作。
Activity管理中有兩個(gè)棧頂:一是ActivityStack的棧頂,它對(duì)應(yīng)到要顯示的TaskRecord; 二是TaskRecord的棧頂,它對(duì)應(yīng)到要顯示的Activity。簡(jiǎn)單來(lái)說(shuō),當(dāng)前顯示的Activity一定是位于其所屬的TaskRecord的棧頂,TaskRecord也一定位于其所屬的ActivityStack的棧頂。
ActivityDisplay
Android支持多屏顯示,在不同的顯示設(shè)備上可以有不同的ActivityStack。
筆者一直在重述:所有的ActivityStack都是通過(guò)ActivityStackSupervisor管理起來(lái)的。 在ActivityStackSupervisor內(nèi)部,設(shè)計(jì)了ActivityDisplay這個(gè)內(nèi)部類(lèi),它對(duì)應(yīng)到一個(gè)顯示設(shè)備,默認(rèn)的顯示設(shè)備是手機(jī)屏幕。 ActivityStackSupervisor間接通過(guò)ActivityDisplay來(lái)維護(hù)多個(gè)ActivityStack的狀態(tài)。 ActivityStack有一個(gè)屬性是mStacks,當(dāng)mStacks不為空時(shí),表示ActivityStack已經(jīng)綁定到了顯示設(shè)備, 其實(shí)ActivityStack.mStacks只是一個(gè)副本,真正的對(duì)象在ActivityDisplay中。
| 屬性 | 描述 |
|---|---|
| mDisplayId | 顯示設(shè)備的唯一標(biāo)識(shí) |
| mDisplay | 獲取顯示設(shè)備信息的工具類(lèi), |
| mDisplayInfo | 顯示設(shè)備信息的數(shù)據(jù)結(jié)構(gòu),包括類(lèi)型、大小、分辨率等 |
| mStacks | 綁定到顯示設(shè)備上的ActivityStack |
| 行為 | 描述 |
|---|---|
| attachActivities() | 將一個(gè)ActivityStack綁定到顯示設(shè)備 |
| setVisibleBehindActivity() | 設(shè)置后臺(tái)顯示的Activity |
| moveHomeStack() | 移動(dòng)HomeStack |
ActivityContainer
在ActivityStackSupervisor中,還設(shè)計(jì)了名為ActivityContainer的內(nèi)部類(lèi)。 該類(lèi)是對(duì)ActivityStack的封裝,相當(dāng)于開(kāi)了一個(gè)后門(mén),可以通過(guò)adb shell am命令對(duì)ActivityStack進(jìn)行讀寫(xiě)操作,方便開(kāi)發(fā)和調(diào)試。
ActivityStackSupervisor
ActivityStackSupervisor的職責(zé)是管理多個(gè)ActivityStack。
| 屬性 | 描述 |
|---|---|
| mHomeStack | 主屏(桌面)所在ActivityStack |
| mFocusedStack | 表示焦點(diǎn)ActivityStack,它能夠獲取用戶(hù)輸入 |
| mLastFocusedStack | 上一個(gè)焦點(diǎn)ActivityStack |
| mActivityDisplays | 表示當(dāng)前的顯示設(shè)備,ActivityDisplay中綁定了若干ActivityStack。通過(guò)該屬性就能間接獲取所有ActivityStack的信息 |
| 行為 | 描述 |
|---|---|
| setFocusedStack() | 設(shè)置當(dāng)前的焦點(diǎn)ActivityStack |
| adjustStackFocus() | |
| startHomeActivity() | 啟動(dòng)桌面 |
ActivityStackSupervisor有很多與ActivityStack功能類(lèi)似的行為,不過(guò)都是針對(duì)多個(gè)ActivityStack進(jìn)行操作。 譬如findTaskLocked(), findActivityLocked(), topRunningActivityLocked(), ensureActivitiesVisibleLocked()等,
場(chǎng)景 1 在啟動(dòng)一個(gè)新的Activity時(shí),需要設(shè)置當(dāng)前的焦點(diǎn),通過(guò)AMS.setFocusedActivityLocked()函數(shù),就能設(shè)置一個(gè) ActivityRecord為當(dāng)前的焦點(diǎn)Activity:
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
final void setFocusedActivityLocked(ActivityRecord r, String reason) {
if (mFocusedActivity != r) {
mFocusedActivity = r;
...
mStackSupervisor.setFocusedStack(r, reason + " setFocusedActivity");
if (r != null) {
mWindowManager.setFocusedApp(r.appToken, true);
}
applyUpdateLockStateLocked(r);
}
...
}
</figure>
該函數(shù)的邏輯很簡(jiǎn)單,如果當(dāng)前的焦點(diǎn)(mFocusedActivity)不是待顯示的(r),則需要更新焦點(diǎn); 然后,就發(fā)起了其他函數(shù)調(diào)用。 這里,需要通過(guò)ActivityStackSupervisor完成對(duì)ActivityStack的管理,通過(guò)調(diào)用setFocusedStack()來(lái)設(shè)置當(dāng)前的焦點(diǎn)Stack, 焦點(diǎn)Stack就是焦點(diǎn)Activity所屬的ActivityStack。
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
void setFocusedStack(ActivityRecord r, String reason) {
if (r != null) {
final TaskRecord task = r.task;
// 判斷輸入的ActivityRecord是否為HomeActivity
boolean isHomeActivity = !r.isApplicationActivity();
if (!isHomeActivity && task != null) {
isHomeActivity = !task.isApplicationTask();
}
if (!isHomeActivity && task != null) {
final ActivityRecord parent = task.stack.mActivityContainer.mParentActivity;
isHomeActivity = parent != null && parent.isHomeActivity();
}
moveHomeStack(isHomeActivity, reason);
}
}
</figure>
只有前臺(tái)的ActivityStack才能獲取焦點(diǎn),所以,該函數(shù)的功能就是要將待顯示的Activity所在的ActivityStack挪到前臺(tái)。 很重要的一個(gè)處理邏輯就是判定待顯示的ActivityRecord的類(lèi)型是否為HomeActivity,判定細(xì)節(jié)此處不表。結(jié)果是: 如果待顯示的ActivityRecord類(lèi)型為HomeActivity,則需要將HomeStack挪到前臺(tái); 否則,意味著要將HomeStack挪到后臺(tái)。 挪動(dòng)HomeStack,是通過(guò)moveHomeStack()這個(gè)函數(shù)實(shí)現(xiàn)的:
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
void moveHomeStack(boolean toFront, String reason) {
// 1\. 獲取當(dāng)前的Top Stack
ArrayList<ActivityStack> stacks = mHomeStack.mStacks;
final int topNdx = stacks.size() - 1;
if (topNdx <= 0) {
return;
}
ActivityStack topStack = stacks.get(topNdx);
// 2\. 判定HomeStack是否需要挪動(dòng)
final boolean homeInFront = topStack == mHomeStack;
if (homeInFront != toFront) {
mLastFocusedStack = topStack;
stacks.remove(mHomeStack);
stacks.add(toFront ? topNdx : 0, mHomeStack);
mFocusedStack = stacks.get(topNdx);
}
...
// 3\. 判定當(dāng)前AMS是否完成啟動(dòng)
if (mService.mBooting || !mService.mBooted) {
final ActivityRecord r = topRunningActivityLocked();
if (r != null && r.idle) {
checkFinishBootingLocked();
}
}
}
</figure>
獲取當(dāng)前的Top Stack,其實(shí)就是獲取mStacks這個(gè)數(shù)組最后的元素。mStacks這個(gè)屬性在ActivityStack和ActivityDisplay中都見(jiàn)過(guò),它們是同一個(gè)東西,ActivityStackSupervisor要管理的就是這個(gè)東西;
-
判定當(dāng)前HomeStack是否需要挪動(dòng)。有四種情況:
- homeInFront = true, toFront = false: 表示HomeStack在前臺(tái),要將其挪到后臺(tái),則需要將HomeStack挪到mStacks的0號(hào)位置;
- homeInFront = true, toFront = true: 表示HomeStack在前臺(tái),要將其挪到前臺(tái),則不需要對(duì)mStacks進(jìn)行調(diào)整;
- homeInFront = false, toFront = true: 表示HomeStack在后臺(tái),要將其挪到前臺(tái),則需要將HomeStack挪到mStacks的末尾;
- homeInFront = false, toFront = false: 表示HomeStack在后臺(tái),要將其挪到后臺(tái),則不需要對(duì)mStacks進(jìn)行調(diào)整。
判斷當(dāng)前AMS是否完成啟動(dòng)。如果當(dāng)前是剛開(kāi)機(jī),AMS都還未啟動(dòng)完成,需要顯示的Activity還處于idle狀態(tài),則需要發(fā)起一次是否啟動(dòng)完成的檢查
ProcessRecord
AMS采用ProcessRecord這個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)進(jìn)程運(yùn)行時(shí)的狀態(tài)信息,當(dāng)創(chuàng)建系統(tǒng)進(jìn)程(system_process)或應(yīng)用進(jìn)程的時(shí)候,就會(huì)通過(guò)AMS初始化一個(gè)ProcessRecord。
| 屬性 | 描述 |
|---|---|
| BatteryStats | 電量統(tǒng)計(jì)的接口 |
| ApplicationInfo | 系統(tǒng)進(jìn)程的ApplicationInfo是從android包中解析出來(lái)的數(shù)據(jù); 應(yīng)用程序的ApplicationInfo是從AndroidManifest.xml中解析出來(lái)的數(shù)據(jù) |
| Process Name | 進(jìn)程名稱(chēng) |
| UID | 進(jìn)程的UID。系統(tǒng)進(jìn)程的UID是1000(Process.SYSTEM_UID); 應(yīng)用進(jìn)程的UID是從10000(Process.FIRST_APPLICATION_UID)開(kāi)始分配的 |
| maxAdj, curAdj, setAdj | 各種不同的OOM Adjustment值 |
| lastPss, lastPssTime | 物理內(nèi)存(PSS)相關(guān),進(jìn)程中有對(duì)象創(chuàng)建或銷(xiāo)毀時(shí),PSS相關(guān)的屬性會(huì)被更新。 |
| activities, services, receivers | 進(jìn)程中的Android組件,隨著進(jìn)程的運(yùn)行,這些信息都可能需要更新。譬如Activity的啟動(dòng)時(shí),ProcessRecord.activies會(huì)增加一個(gè)實(shí)例; 銷(xiāo)毀時(shí),對(duì)將對(duì)應(yīng)的實(shí)例從activities刪除 |
| pkgList | 進(jìn)程中運(yùn)行的包 |
| thread | 該屬性是IApplicationThread類(lèi)型的對(duì)象 |
ProcessRecord有“激活(Active)”和“非激活(Inactive)”兩種狀態(tài),只有將ProcessRecord綁定到一個(gè)實(shí)際進(jìn)程的時(shí)候,才是激活狀態(tài)。 綁定成功后,thread屬性就被賦值,表示ProcessRecord已經(jīng)激活。 激活后,AMS就可以通過(guò)這個(gè)接口完成對(duì)應(yīng)用進(jìn)程的管理,譬如啟動(dòng)Activity、派發(fā)廣播等。 將ProcessRecord綁定到應(yīng)用進(jìn)程的過(guò)程在Android四大組件之Activity–應(yīng)用進(jìn)程與系統(tǒng)進(jìn)程的通信一文中有詳細(xì)的分析。
| 行為 | 描述 |
|---|---|
| makeActive() | 將ProcessRecord置成激活狀態(tài) |
| makeInactive() | 將ProcessRecord置成非激活狀態(tài) |
| addPackage() | 向ProcessRecord添加包 |
2.2 關(guān)聯(lián)關(guān)系

- AMS運(yùn)行在SystemServer進(jìn)程中。SystemServer進(jìn)程啟動(dòng)時(shí),會(huì)通過(guò)SystemServer.startBootstrapServices()來(lái)創(chuàng)建一個(gè)AMS的對(duì)象;
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
private void startBootstrapServices() {
...
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
...
}
</figure>
- AMS通過(guò)ActivityStackSupervisor來(lái)管理Activity。AMS對(duì)象只會(huì)存在一個(gè),在初始化的時(shí)候,會(huì)創(chuàng)建一個(gè)唯一的ActivityStackSupervisor對(duì)象;
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
public ActivityManagerService(Context systemContext) {
...
mStackSupervisor = new ActivityStackSupervisor(this);
...
}
</figure>
- ActivityStackSupervisor中維護(hù)了顯示設(shè)備的信息。當(dāng)有新的顯示設(shè)備添加時(shí),會(huì)創(chuàng)建一個(gè)新的ActivityDisplay對(duì)象;
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
public void handleDisplayAddedLocked(int displayId) {
...
newDisplay = mActivityDisplays.get(displayId) == null;
if (newDisplay) {
ActivityDisplay activityDisplay = new ActivityDisplay(displayId);
...
}
...
}
</figure>
- ActivityStack與顯示設(shè)備的綁定。當(dāng)需要?jiǎng)?chuàng)建一個(gè)ActivityStack時(shí),需要將其綁定到具體的顯示設(shè)備。 ActivityStackSupervisor通過(guò)ActivityContainer這個(gè)內(nèi)部類(lèi)對(duì)ActivityStack進(jìn)行了一層封裝, 所以,會(huì)首先創(chuàng)建一個(gè)ActivityContainer對(duì)象;然后,通過(guò)ActivityContainer.attachToDisplayLocked()函數(shù)進(jìn)行具體的綁定操作;
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
private int createStackOnDisplay(int stackId, int displayId) {
...
ActivityContainer activityContainer = new ActivityContainer(stackId);
mActivityContainers.put(stackId, activityContainer);
activityContainer.attachToDisplayLocked(activityDisplay);
return stackId;
}
</figure>
- AMS維護(hù)了所有進(jìn)程的信息ProcessRecord。當(dāng)需要?jiǎng)?chuàng)建一個(gè)新的進(jìn)程時(shí), 會(huì)通過(guò)AMS.newProcessRecordLocked()函數(shù)來(lái)創(chuàng)建一個(gè)ProcessRecord對(duì)象, ProcessRecord對(duì)象都保存在AMS.mPidsSelfLocked這個(gè)屬性中;
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid) {
...
return new ProcessRecord(stats, info, proc, uid);
}
</figure>
- 通過(guò)ActivityStackSupervisor來(lái)創(chuàng)建ActivityRecord。當(dāng)SystemServer進(jìn)程收到來(lái)自應(yīng)用進(jìn)程的啟動(dòng)Activity請(qǐng)求時(shí), 會(huì)通過(guò)ActivityStackSupervisor來(lái)創(chuàng)建一個(gè)ActivityRecord對(duì)象;
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
final int startActivityLocked(IApplicationThread caller, ...) {
...
ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
requestCode, componentSpecified, this, container, options);
...
}
</figure>
- 在ActivityStack上創(chuàng)建TaskRecord。當(dāng)需要?jiǎng)?chuàng)建新的任務(wù)棧時(shí),就會(huì)通過(guò)ActivityStack對(duì)象來(lái)創(chuàng)建一個(gè)TaskRecord對(duì)象, 這樣就建立了ActivityStack和TaskRecord的關(guān)聯(lián);
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
boolean toTop) {
TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession,
voiceInteractor);
addTask(task, toTop, false);
return task;
}
</figure>
- ActivityRecord的宿主TaskRecord。每一個(gè)ActivityRecord都需要找到自己的宿主TaskRecord,通過(guò)ActivityRecord.setTask()函數(shù) 就能建立ActivityRecord和TaskRecord的關(guān)聯(lián);
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
void setTask(TaskRecord newTask, TaskRecord taskToAffiliateWith) {
...
task = newTask;
setTaskToAffiliateWith(taskToAffiliateWith);
}
</figure>
- 進(jìn)程中運(yùn)行的Activity信息。Activity在應(yīng)用進(jìn)程中運(yùn)行,AMS中記錄了進(jìn)程中所有運(yùn)行的Activity的信息,在ActivityRecord創(chuàng)建后, 會(huì)通過(guò)ProcessRecord.addPackage()函數(shù),在ProcessRecord中登記ActivityRecord的信息
<figure class="highlight" style="box-sizing: border-box; display: block; margin: 0px; color: rgb(51, 51, 51); font-family: Menlo, Monaco, Consolas, "Courier New", monospace, Helvetica, Tahoma, Arial, "WenQuanYi Micro Hei", 文泉驛微米黑, STXihei, 華文細(xì)黑, "Microsoft YaHei", 微軟雅黑, SimSun, 宋體, Heiti, 黑體, sans-serif; font-size: 17px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(248, 248, 253); text-decoration-style: initial; text-decoration-color: initial;">
void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
...
app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
...
}
</figure>
3. Activity管理的延伸
在分析完Activity管理的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)及關(guān)聯(lián)關(guān)系后,想必各位讀者已經(jīng)感受到了Activity管理的復(fù)雜性。 如此龐大而精密的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì),是在什么背景下產(chǎn)生的呢?從已有的Activity設(shè)計(jì)中, 能否窺探出以后Android在Activity相關(guān)特性的發(fā)展方向呢?譬如多屏幕、多窗口的Activity顯示。
筆者一直認(rèn)為,研究Android源碼不僅僅是理解Android的內(nèi)部運(yùn)行機(jī)制,更重要的是體會(huì)出其背后的設(shè)計(jì)思想, 總結(jié)出一套解決同類(lèi)問(wèn)題的方法論,然后再到具體的軟件開(kāi)發(fā)中進(jìn)行實(shí)踐,哪怕不在Android平臺(tái)下開(kāi)發(fā), 提煉出來(lái)的方法論仍然是受用的。
3.1 設(shè)計(jì)思想
- TaskRecord是一個(gè)棧,ActivityStack也是一個(gè)棧,Android這么設(shè)計(jì)有什么好處嗎?