Android四大組件之Activity--管理方式(轉(zhuǎn))

文章轉(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)的概覽圖:

Maintenace Guideline

圖中的方框可以理解為一個(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ò)程:

  1. 移除任務(wù)棧中已有的ActivityRecord對(duì)象,即任務(wù)棧中不會(huì)出現(xiàn)兩個(gè)同樣的ActivityRecord對(duì)象。此處需要注意,兩次啟動(dòng)同一個(gè)Activity,是會(huì)產(chǎn)生兩個(gè)不同的ActivityRecord對(duì)象的;

  2. 如果任務(wù)棧為空,則設(shè)置任務(wù)棧的初始狀態(tài),否則,設(shè)置ActivityRecord的類(lèi)型為任務(wù)棧的類(lèi)型。由此可見(jiàn),同一個(gè)任務(wù)棧中,所有ActivityRecord的類(lèi)型都是一樣的,而且是由任務(wù)棧的第一個(gè)ActivityRecord的類(lèi)型決定的;

  3. 此處的位置就是任務(wù)棧頂,也就是mActivities屬性的末尾;

  4. 任務(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>

  1. 獲取當(dāng)前的Top Stack,其實(shí)就是獲取mStacks這個(gè)數(shù)組最后的元素。mStacks這個(gè)屬性在ActivityStack和ActivityDisplay中都見(jiàn)過(guò),它們是同一個(gè)東西,ActivityStackSupervisor要管理的就是這個(gè)東西;

  2. 判定當(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)整。
  3. 判斷當(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)系

Maintenace Guideline
  • 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ì)有什么好處嗎?
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容