Android四大組件之Activity--管理方式

1. 概覽

Activity的管理有靜態和動態兩層涵義:java

  • 靜態是指Activity的代碼組織結構,即Application中聲明的Activity的集合,這些Activity被組織在一個APK中,有特定的包名。 在編寫應用程序時,Activity對應到用戶界面,它定義了用戶界面的佈局、交互行爲、啓動方式等,最重要的,是Activity的生命週期函數。 在應用進程看來,只須要按照Android定義的規範,實現生命週期函數的具體邏輯便可,全部的用戶界面都遵循同一個規範。 編寫完一個應用程序的全部用戶界面,就算是完成了Activity的靜態管理。android

  • 動態是指Activity的運行調度方式,即Activity生命週期的執行過程當中,內部數據結構的變化,Android對全部Activity進行統一管理。 在一個應用程序安裝時,系統會解析出APK中全部Activity的信息,當顯示APK中的用戶界面時,就須要調度Activity的生命週期函數了。 系統進程(system_process)中維護了全部Activity的狀態,管理中樞就是ActivityManagerService,Android爲此作了精密的設計,採用 棧 做爲基本的數據結構。git

本文分析的Activity管理方式屬於動態這個層面,也就是系統進程中針對Activity的調度機制。github

2. Activity管理的基礎

Activity的管理離不開基礎的數據結構以及它們之間的相互關聯, 因此,筆者會從基礎的數據結構出發,分析類的屬性和行爲,並結合一些場景進行源碼分析; 進一步,會分析各個類之間關聯關係的構建過程。 這樣一來,整個Activity管理運轉的模型就清楚了,這個模型承載的不少業務,本文不會具體展開, 在Android四大組件之Activity–啓動過程一文中,筆者會再詳細介紹一種典型的業務。shell

2.1 數據結構

Activity管理相關的數據結構包括:數組

這些數據結構都是Java類,它們都屬於系統進程的範疇,即對象的構建和銷燬都在系統進程中完成,筆者將從類的屬性和行爲這兩個角度來分析類的職能。 Android有一些約定俗成的函數命名方式,與Activity管理相關不少函數都會帶有Locked後綴,表示這些函數須要進行多線程同步操做(synchronized),它們會讀/寫一些多線程共享的數據,讀者在分析代碼的時候能夠適當關注。瀏覽器

先上一張數據結構的概覽圖:數據結構

Maintenace Guideline

圖中的方框能夠理解爲一箇中包含關係:譬如一個TaskRecord中包含多個ActivityRecord; 圖中的鏈接線能夠理解爲等價關係,譬如同一個ActivityRecord會被TaskRecord和ProcessRecord引用,二者是從不一樣維度來管理ActivityRecord。多線程

  • ActivityRecord是Activity管理的最小單位,它對應着一個用戶界面;
  • TaskRecord也是一個棧式管理結構,每個TaskRecord均可能存在一個或多個ActivityRecord,棧頂的ActivityRecord表示當前可見的界面;
  • ActivityStack是一個棧式管理結構,每個ActivityStack均可能存在一個或多個TaskRecord,棧頂的TaskRecord表示當前可見的任務;
  • ActivityStackSupervisor管理着多個ActivityStack,但當前只會有一個獲取焦點(Focused)的ActivityStack;
  • ProcessRecord記錄着屬於一個進程的全部ActivityRecord,運行在不一樣TaskRecord中的ActivityRecord多是屬於同一個 ProcessRecord。

ActivityRecord

ActivityRecord是AMS調度Activity的基本單位,它須要記錄AndroidManifest.xml中所定義Activity的靜態特徵,同時, 也須要記錄Activity在被調度時的狀態變化,所以ActivityRecord這個類的屬性比較多。app

屬性 描述
ActivityInfo 從<activity>標籤中解析出來的信息,包含launchMode,permission,taskAffinity等
mActivityType Activity的類型有三種:APPLICATION_ACTIVITY_TYPE(應用)、HOME_ACTIVITY_TYPE(桌面)、RECENTS_ACTIVITY_TYPE(最近使用)
appToken 當前ActivityRecord的標識
packageName 當前所屬的包名,這是由<activity>靜態定義的
processName 當前所屬的進程名,大部分狀況都是由<activity>靜態定義的,但也有例外
taskAffinity 相同taskAffinity的Activity會被分配到同一個任務棧中
intent 啓動當前Activity的Intent
launchedFromUid 啓動當前Activity的UID,即發起者的UID
launchedFromPackage 啓動當前Activity的包名,即發起者的包名
resultTo 在當前ActivityRecord看來,resultTo表示上一個啓動它的ActivityRecord,當須要啓動另外一個ActivityRecord,會把本身做爲resultTo,傳遞給下一個ActivityRecord
state ActivityRecord所處的狀態,初始值是ActivityState.INITIALIZING
app ActivityRecord的宿主進程
task ActivityRecord的宿主任務
inHistory 標識當前的ActivityRecord是否已經置入任務棧中
frontOfTask 標識當前的ActivityRecord是否處於任務棧的根部,便是否爲進入任務棧的第一個ActivityRecord
newIntents Intent數組,用於暫存尚未調度到應用進程Activity的Intent

因爲ActivityRecord是一個最基本的數據結構,因此其行爲相對較少,大都是一些用於斷定和更新當前ActivityRecord狀態的函數:

行爲 描述
putInHistory(), takeFromHistory(), isInHistory() 基於inHistory屬性,來斷定和更新ActivityRecord是否在任務棧的狀態值
isHomeActivity(), isRecentsActivity(), isApplicationActivity() 基於mActivityType屬性,斷定Activity的類型
setTask() 設置ActivityRecord的宿主任務
deliverNewIntentLocked() 向當前ActivityRecord繼續派發Intent。在一些場景下,位於任務棧頂的ActivityRecord會繼續接受新的Intent(譬如以singleTop方式啓動的同一個Activity),這時候,會觸發調度Activity.onNewIntent()函數
addNewIntentLocked() 若是Intent沒有派發到應用進程,則經過該函數往newIntents數組中添加一個元素。

要理解ActivityRecord這個數據結構,能夠從其構造函數出發,理解其屬性在什麼場景下會發生變化。 每次須要啓動一個新的Activity時,都會構建一個ActivityRecord對象,這在ActivityStackSupervisor.startActivityLocked()函數中完成,構造一個ActivityRecord要傳入的參數是至關多的:

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對象 appToken = new Token(this); //appToken能夠進行跨進程傳遞,標識一個AR對象 info = aInfo; //從AndroidManifest.xml中解析獲得的Activity信息 launchedFromUid = _launchedFromUid; //譬如從瀏覽器啓動當前AR,那麼該屬性記錄的就是瀏覽器的UID launchedFromPackage = _launchedFromPackage; userId = UserHandle.getUserId(aInfo.applicationInfo.uid); intent = _intent; //啓動當前AR的Intent shortComponentName = _intent.getComponent().flattenToShortString(); resolvedType = _resolvedType; componentSpecified = _componentSpecified; configuration = _configuration; resultTo = _resultTo; //記錄上一個AR對象 resultWho = _resultWho; //reslutTo的字符串標識 requestCode = _reqCode; //上一個AR對象設定的Request Code state = ActivityState.INITIALIZING; //AR的狀態,Activity調度時發生改變 frontOfTask = false; //是否處於Task的根部,調整任務棧中AR順序時,可能發生改變 launchFailed = false; stopped = false; //pause操做完成狀態位 delayedResume = false; finishing = false; //stoped到finished之間的過渡狀態位 configDestroy = false; keysPaused = false; //若是置爲true,則當前AR再也不接受用戶輸入 inHistory = false; //將AR壓入任務棧後,該狀態位被置爲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) { //根據aInfo給realActivity, taskAffinity, processName等屬性賦值 ... } else { //沒有aInfo的狀況下,賦予默認值 realActivity = null; taskAffinity = null; stateNotNeeded = false; appInfo = null; processName = null; packageName = null; fullscreen = true; noDisplay = false; mActivityType = APPLICATION_ACTIVITY_TYPE; immersive = false; } }

TaskRecord

TaskRecord的職責是管理多個ActivityRecord,本文所述的任務、任務棧指的就是TaskRecord。 啓動Activity時,須要找到Activity的宿主任務,若是不存在,則須要新建一個,也就是說全部的ActivityRecord都必須有宿主。 TaskRecord與ActivityRecord是一對多的關係,TaskRecord的屬性中包含了ActivityRecord的數組; 同時,TaskRecord還須要維護任務棧自己的狀態。

屬性 描述
taskid TaskRecord的惟一標識
taskType 任務棧的類型,等同於ActivityRecord的類型,是由任務棧的第一個ActivityRecord決定的
intent 在當前任務棧中啓動的第一個Activity的Intent將會被記錄下來,後續若是有相同的Intent時,會與已有任務棧的Intent進行匹配,若是匹配上了,就不須要再新建一個TaskRecord了
realActivity, origActivity 啓動任務棧的Activity,這兩個屬性是用包名(CompentName)表示的,real和orig是爲了區分Activity有無別名(alias)的狀況,若是AndroidManifest.xml中定義的Activity是一個alias,則此處real表示Activity的別名,orig表示真實的Activity
affinity TaskRecord把Activity的affinity記錄下來,後續啓動Activity時,會從已有的任務棧中匹配affinity,若是匹配上了,則不須要新建TaskRecord
rootAffinity 記錄任務棧中最底部Activity的affinity,一經設定後就再也不改變
mActivities 這是TaskRecord最重要的一個屬性,TaskRecord是一個棧結構,棧的元素是ActivityRecord,其內部實現是一個數組mActivities
stack 當前TaskRecord所在的ActivityStack

TaskRecord的行爲側重在TaskRecord自己的管理:增/刪/改/查任務棧中的元素。

行爲 描述
getRootActivity(), getTopActivity() 任務棧有根部(Root)和頂部(Top),能夠經過這兩個函數分別獲取到根部和頂部的ActivityRecord。獲取的過程就是對TaskRecord.mActivities進行遍歷,若是ActivityRecord的狀態不是finishing,就認爲是有效的ActivityRecord
topRunningActivityLocked() 雖然也是從頂至底對任務棧進行遍歷獲取頂部的ActivityRecord,但這個函數同getTopActivity()有區別:輸入參數notTop,表示在遍歷的過程當中須要排除notTop這個ActivityRecord;
addActivityToTop(), addActivityAtBottom() 將ActivityRecord添加到任務棧的頂部或底部
moveActivityToFrontLocked() 該函數將一個ActivityRecord移至TaskRecord的頂部,實現方法就是先刪除已有的,再在棧頂添加一個新的
setFrontOfTask() ActivityRecord有一個屬性是frontOfTask,表示ActivityRecord是否爲TaskRecord的根Activity。該函數設置TaskRecord中全部ActivityRecord的frontOfTask屬性,從棧底往上開始遍歷,第一個不處於finishing狀態的ActivityRecord的frontOfTask屬性置成true,其餘都爲false
performClearTaskLocked() 清除TaskRecord中的ActivityRecord。當啓動Activity時,用了Intent.FLAG_ACTIVITY_CLEAR_TOP參數,那麼在宿主任務中,待啓動ActivityRecord之上的其餘ActivityRecord都會被清除

僅僅把類的屬性和行爲羅列出來,固然不足以理解TaskRecord的工做原理。 接下來,將深刻部分函數的代碼,分析TaskRecord在一些場景下的具體執行邏輯。

場景 1 啓動一個Activity時,一般須要將ActivityRecord壓入任務棧頂,addActivityToTop()就是爲此功能設計:

void addActivityToTop(ActivityRecord r) { addActivityAtIndex(mActivities.size(), r); }

將ActivityRecord壓入棧頂,其實就是在mActivities數組末尾添加一個元素,因此,實際壓入棧頂的操做是由addActivityAtIndex()完成:

void addActivityAtIndex(int index, ActivityRecord r) { // 1. 移除已有的ActivityRecord對象 if (!mActivities.remove(r) && r.fullscreen) { numFullscreen++; } // 2. 根據任務棧是否爲空,設置狀態 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. 更新任務棧關聯的Intent updateEffectiveIntent(); ... }

該函數會通過如下處理過程:

  1. 移除任務棧中已有的ActivityRecord對象,即任務棧中不會出現兩個一樣的ActivityRecord對象。此處須要注意,兩次啓動同一個Activity,是會產生兩個不一樣的ActivityRecord對象的;

  2. 若是任務棧爲空,則設置任務棧的初始狀態,不然,設置ActivityRecord的類型爲任務棧的類型。因而可知,同一個任務棧中,全部ActivityRecord的類型都是同樣的,並且是由任務棧的第一個ActivityRecord的類型決定的;

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

  4. 任務棧中元素髮生了變化,因此須要更新任務棧關聯的Intent,這是經過調用updateEffectiveIntent()實現的,函數的具體邏輯,在場景 3中再行分析。

場景 2 當待顯示的Activity壓入任務棧後,就須要設置棧頂ActivityRecord的狀態,這時候,會調用topRunningActivityLocked()函數來獲取棧頂的元素,爲了更好的分析topRunningActivityLocked()的使用場景,筆者把另外一個與其功能類似的函數getTopActivity()也列出來:

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狀態之外,還要求不是當前給定輸入的ActivityRecord if (!r.finishing && r != notTop && stack.okToShowLocked(r)) { return r; } } }

二者是從頂到底對任務棧進行遍歷,但實現邏輯不一樣,topRunningActivityLocked()接受一個輸入參數notTop,在尋找時,要求排除notTop指定的ActivityRecord,一般,傳入的notTop都是null,隱含的意思就是棧頂的ActivityRecord尚未被銷燬。從函數命名topRunning,也能夠看出其與getTop的區別:getTop無論棧頂的死活,拿到就好; topRunning要求拿到的必定是活的棧頂。

另外,topRunningActivityLocked()還有一個限制條件: ActivityRecord是能夠被顯示的(okToShow),這是經過ActivityStack.okToShowLocked()來斷定的,主要是爲了應多多用戶或鎖屏顯示的Activity,通常狀況下,函數返回值都爲true。

場景 3 假定在啓動一個Activity時,設置了Intent的FLAG_ACTIVITY_REORDER_TO_FRONT,表示要將Activity重排序到任務棧頂。 若是目標的Activity在任務棧中已經啓動過,則須要將其挪至棧頂。譬如目標任務棧從底到頂是 A - B - C, 而後,又以FLAG_ACTIVITY_REORDER_TO_FRONT啓動了 A,那最終任務棧會變化爲 B - C - A 。 這就會調用到moveActivityToFrontLocked()函數:

final void moveActivityToFrontLocked(ActivityRecord newTop) { mActivities.remove(newTop); mActivities.add(newTop); updateEffectiveIntent(); setFrontOfTask(); }

該函數須要調整任務棧中ActivityRecord的順序,延用上述例子, A 將做爲參數newTop。 首先,會將 A 從任務棧中移除; 而後,再將 A 添加到任務棧頂; 接着,會調用updateEffectiveIntent()函數來更新任務棧關聯的Intent:

void updateEffectiveIntent() { final int effectiveRootIndex = findEffectiveRootIndex(); final ActivityRecord r = mActivities.get(effectiveRootIndex); setIntent(r); }

該函數會找到一個有效的Root Index,即任務棧底部的索引,根據這個索引值取出對應的ActivityRecord。 延續上述例子,B 會被調整爲任務棧的根部ActivityRecord,經過調用setIntent()來設置當前任務棧關聯的Intent爲啓動 B 的Intent,然而,這裏可不止改變TaskRecord.intent這一個屬性這個簡單,與Taskrecord的發起者相關的屬性值都要更改, 譬如mCallingUid,mCallingPackage都得更改成 B 的發起者:

void setIntent(ActivityRecord r) { setIntent(r.intent, r.info); mCallingUid = r.launchedFromUid; mCallingPackage = r.launchedFromPackage; }

這裏還有一個重載的setIntent()函數,再也不展開分析了,只須要知道諸如affinity, realActivity等屬性都會被重置便可。

更新完TaskRecord的Intent,再回到moveActivityToFrontLocked()函數中,須要繼續更新任務棧的Front。 以前任務棧的Front是 A,在發生變化後, Front須要更新爲 B,然而,TaskRecord並無一個屬性用來記錄當前的Front, 它是根據任務棧中每個ActivityRecord的frontOfTask屬性來斷定的:

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的這個屬性都置爲false r.frontOfTask = false; } else { // 不爲finishing狀態,表示已經找到了front的ActivityRecord r.frontOfTask = true; foundFront = true; } if (!foundFront && numActivities > 0) { mActivities.get(0).frontOfTask = true; } }

該函數從底到頂對任務棧進行遍歷,找到的第一個未結束(finishing = faulse)的ActivityRecord, 將其frontOfTask屬性設置成true;其餘全部ActivtyRecord的frontOfTask屬性設置爲false。

ActivityStack

ActivityStack的職責是管理多個任務棧(TaskRecord),它是一個棧式結構,棧中的元素是TaskRecord。 每一個Activity在特定的時刻都會有一個狀態,譬如顯示、銷燬等, 在應用進程看來,這些狀態的變化就是在執行Activity的生命週期函數; 在系統進程看來,這些狀態的變化都須要通過ActivityStack來驅動。 Activity的狀態是經過ActivityState這個枚舉類來定義的:

enum ActivityState { INITIALIZING, RESUMED, PAUSING, PAUSED, STOPPING, STOPPED, FINISHING, DESTROYING, DESTROYED }

從INITIALIZING到DESTROYED,所定義狀態值示意了Activity生命週期的走向。

屬性 描述
stackId 每個ActivityStack都有一個編號,從0開始遞增。編號爲0,表示桌面(Launcher)所在的ActivityStack,叫作Home Stack
mTaskHistory TaskRecord數組,ActivityStack棧就是經過這個數組實現的
mPausingActivity 在發生Activity切換時,正處於Pausing狀態的Activity
mResumedActivity 當前處於Resumed狀態的ActivityRecord
mStacks ActivityStack會綁定到一個顯示設備上,譬如手機屏幕、投影儀等,在AMS中,經過ActivityDisplay這個類來抽象表示一個顯示設備,ActivityDisplay.mStacks表示當前已經綁定到顯示設備的全部ActivityStack。當執行一次綁定操做時,就會將ActivityStack.mStacks這個屬性賦值成ActivityDisplay.mStacks,不然,ActivityStack.mStacks就爲null。簡而言之,當mStacks不爲null時,表示當前ActivityStack已經綁定到了一個顯示設備

Activity狀態的變遷,不只僅是給ActivityRecord.state賦一個狀態值那麼簡單,ActivityStack要對棧進行調整:以前的Activity要銷燬或者挪到後臺,待顯示的Activity要挪到棧頂,這一調整,涉及到的工做就多了。

行爲 描述
findTaskLocked() 該函數的功能是找到目標ActivityRecord(target)所在的任務棧(TaskRecord),若是找到,則返回棧頂的ActivityRecord,不然,返回null
findActivityLocked() 根據Intent和ActivityInfo這兩個參數能夠獲取一個Activity的包名,該函數會從棧頂至棧底遍歷ActivityStack中的全部Activity,若是包名匹配成功,就返回
moveToFront) 該函數用於將當前的ActivityStack挪到前臺,執行時,調用ActivityStack中的其餘一些斷定函數
isAttached() 用於斷定當前ActivityStack是否已經綁定到顯示設備
isOnHomeDisplay() 用於斷定當前是否爲默認的顯示設備(Display.DEFAULT_DISPLAY),一般,默認的顯示設備就是手機屏幕
isHomeStack() 用於斷定當前ActivityStack是否爲Home Stack,即斷定當前顯示的是否爲桌面(Launcher)
moveTaskToFrontLocked() 該函數用於將指定的任務棧挪到當前ActivityStack的最前面。在Activity狀態變化時,須要對已有的ActivityStack中的任務棧進行調整,待顯示Activity的宿主任務須要挪到前臺
insertTaskAtTop() 將任務插入ActivityStack棧頂

ActivityStack還有不少與遷移Activity狀態相關的行爲: startActivityLocked(), resumeTopActivityLocked(), completeResumeLocked(), startPausingLocked(), completePauseLocked(), stopActivityLocked(), activityPausedLocked(), finishActivityLocked(), activityDestroyedLocked(), 它們與Activity的生命週期調度息息相關,在Android四大組件之Activity–啓動過程一文中,會再詳細分析這幾個函數的實現邏輯,本文仍是經過一個簡單的場景來分析ActivityStack的行爲。

場景 1 以singleTask的方式啓動一個處於後臺的Activity,那麼,就須要將Activity挪到前臺。怎麼挪呢?

第一步,findTaskLocked(): 找到Activity所在TaskRecord;

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; }

該函數的功能是找到target ActivityRecord所在的Task,若是找到,則返回Task棧頂的ActivityRecord,不然,返回null。 主體邏輯是對ActivityStack中的全部Task進行遍歷,如下幾種狀況表示找到了ActivityRecord的宿主task:

  • Affinity相同。rootAffinity表示第一次啓動該task時affinity值,若是一個ActivityRecord的taskAffinity屬性與其相等, 那麼這個task天然是ActivityRecord的宿主;
  • Intent的包名相同。
  • Affinity Intent的包名相同。

第二步,moveToFront(): 將TaskRecord所在的ActivityStack挪到前臺;

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); } } }

首先,會有一些斷定:

  • isAttached(): 只有在當前ActivityStack綁定到顯示設備的狀況下,才須要挪到;
  • isOnHomeDisplay(): 若是當前是默認的顯示設備,則對HomeStack(桌面)進行挪動, 這涉及到對多個ActivityStack的操做,因此須要經過ActivityStackSupervisor完成;
  • isHomeStack(): 若是當前是HomeStack,則將其挪到先後; 不然,將HomeStack挪到後臺

而後,就是對mStacks這個屬性進行操做:在mStacks數組中,刪除已有的ActivityStack對象,並添加一個新的,這樣作其實達到了一個目的,前臺的ActivityStacks處於mStacks末尾。

最後,調用WMS.moveTaskToTop()通知窗口的進行變化調整。

第三步, moveTaskToFrontLocked(): 將TaskRecord挪到ActivityStack的棧頂;

final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord source, Bundle options, String reason) { final int numTasks = mTaskHistory.size(); final int index = mTaskHistory.indexOf(tr); // 斷定ActivityStack是否須要挪動任務棧 if (numTasks == 0 || index < 0) { ... return; } // 調整ActivityStack insertTaskAtTop(tr); moveToFront(reason); ... // 將棧頂的Activity置爲Resumed狀態 mStackSupervisor.resumeTopActivitiesLocked(); }

首先,會通過斷定:若是當前的ActivityStack爲空,或者不存在要挪動的任務,則不須要對當前ActivityStack進行調整;

而後,肯定目標任務在當前ActivityStack中,則對ActivityStack進行調整,將目標任務插入ActivityStack棧頂。

  • insertTaskAtTop(),會先將已有的目標任務刪除,再從新添加到棧頂位置;
  • moveToFront(),在第二步中執行過一次,由於在某些場景下,只會調用moveToFront(),不會調用moveTaskToFrontLocked(); 一旦要將任務挪到ActivityStack棧頂,意味着ActivityStack也必定要挪到前臺;

最後,將任務棧頂的Activity置爲Resumed狀態,這裏是經過ActivityStackSupervisor完成的。由於可能同時存在多個顯示設備,因此須要對多個ActivityStack進行操做。

Activity管理中有兩個棧頂:一是ActivityStack的棧頂,它對應到要顯示的TaskRecord; 二是TaskRecord的棧頂,它對應到要顯示的Activity。簡單來講,當前顯示的Activity必定是位於其所屬的TaskRecord的棧頂,TaskRecord也必定位於其所屬的ActivityStack的棧頂。

ActivityDisplay

Android支持多屏顯示,在不一樣的顯示設備上能夠有不一樣的ActivityStack。

筆者一直在重述:全部的ActivityStack都是經過ActivityStackSupervisor管理起來的。 在ActivityStackSupervisor內部,設計了ActivityDisplay這個內部類,它對應到一個顯示設備,默認的顯示設備是手機屏幕。 ActivityStackSupervisor間接經過ActivityDisplay來維護多個ActivityStack的狀態。 ActivityStack有一個屬性是mStacks,當mStacks不爲空時,表示ActivityStack已經綁定到了顯示設備, 其實ActivityStack.mStacks只是一個副本,真正的對象在ActivityDisplay中。

屬性 描述
mDisplayId 顯示設備的惟一標識
mDisplay 獲取顯示設備信息的工具類,
mDisplayInfo 顯示設備信息的數據結構,包括類型、大小、分辨率等
mStacks 綁定到顯示設備上的ActivityStack
行爲 描述
attachActivities() 將一個ActivityStack綁定到顯示設備
setVisibleBehindActivity() 設置後臺顯示的Activity
moveHomeStack() 移動HomeStack

ActivityContainer

在ActivityStackSupervisor中,還設計了名爲ActivityContainer的內部類。 該類是對ActivityStack的封裝,至關於開了一個後門,能夠經過adb shell am命令對ActivityStack進行讀寫操做,方便開發和調試。

ActivityStackSupervisor

ActivityStackSupervisor的職責是管理多個ActivityStack。

屬性 描述
mHomeStack 主屏(桌面)所在ActivityStack
mFocusedStack 表示焦點ActivityStack,它可以獲取用戶輸入
mLastFocusedStack 上一個焦點ActivityStack
mActivityDisplays 表示當前的顯示設備,ActivityDisplay中綁定了若干ActivityStack。經過該屬性就能間接獲取全部ActivityStack的信息
行爲 描述
setFocusedStack() 設置當前的焦點ActivityStack
adjustStackFocus()  
startHomeActivity() 啓動桌面

ActivityStackSupervisor有不少與ActivityStack功能相似的行爲,不過都是針對多個ActivityStack進行操做。 譬如findTaskLocked(), findActivityLocked(), topRunningActivityLocked(), ensureActivitiesVisibleLocked()等,

場景 1 在啓動一個新的Activity時,須要設置當前的焦點,經過AMS.setFocusedActivityLocked()函數,就能設置一個 ActivityRecord爲當前的焦點Activity:

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); } ... }

該函數的邏輯很簡單,若是當前的焦點(mFocusedActivity)不是待顯示的(r),則須要更新焦點; 而後,就發起了其餘函數調用。 這裏,須要經過ActivityStackSupervisor完成對ActivityStack的管理,經過調用setFocusedStack()來設置當前的焦點Stack, 焦點Stack就是焦點Activity所屬的ActivityStack。

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); } }

只有前臺的ActivityStack才能獲取焦點,因此,該函數的功能就是要將待顯示的Activity所在的ActivityStack挪到前臺。 很重要的一個處理邏輯就是斷定待顯示的ActivityRecord的類型是否爲HomeActivity,斷定細節此處不表。結果是: 若是待顯示的ActivityRecord類型爲HomeActivity,則須要將HomeStack挪到前臺; 不然,意味着要將HomeStack挪到後臺。 挪動HomeStack,是經過moveHomeStack()這個函數實現的:

void moveHomeStack(boolean toFront, String reason) { // 1. 獲取當前的Top Stack ArrayList<ActivityStack> stacks = mHomeStack.mStacks; final int topNdx = stacks.size() - 1; if (topNdx <= 0) { return; } ActivityStack topStack = stacks.get(topNdx); // 2. 斷定HomeStack是否須要挪動 final boolean homeInFront = topStack == mHomeStack; if (homeInFront != toFront) { mLastFocusedStack = topStack; stacks.remove(mHomeStack); stacks.add(toFront ? topNdx : 0, mHomeStack); mFocusedStack = stacks.get(topNdx); } ... // 3. 斷定當前AMS是否完成啓動 if (mService.mBooting || !mService.mBooted) { final ActivityRecord r = topRunningActivityLocked(); if (r != null && r.idle) { checkFinishBootingLocked(); } } }
  1. 獲取當前的Top Stack,其實就是獲取mStacks這個數組最後的元素。mStacks這個屬性在ActivityStack和ActivityDisplay中都見過,它們是同一個東西,ActivityStackSupervisor要管理的就是這個東西;

  2. 斷定當前HomeStack是否須要挪動。有四種狀況:
    • homeInFront = true, toFront = false: 表示HomeStack在前臺,要將其挪到後臺,則須要將HomeStack挪到mStacks的0號位置;
    • homeInFront = true, toFront = true: 表示HomeStack在前臺,要將其挪到前臺,則不須要對mStacks進行調整;
    • homeInFront = false, toFront = true: 表示HomeStack在後臺,要將其挪到前臺,則須要將HomeStack挪到mStacks的末尾;
    • homeInFront = false, toFront = false: 表示HomeStack在後臺,要將其挪到後臺,則不須要對mStacks進行調整。
  3. 判斷當前AMS是否完成啓動。若是當前是剛開機,AMS都還未啓動完成,須要顯示的Activity還處於idle狀態,則須要發起一次是否啓動完成的檢查

ProcessRecord

AMS採用ProcessRecord這個數據結構來維護進程運行時的狀態信息,當建立系統進程(system_process)或應用進程的時候,就會經過AMS初始化一個ProcessRecord。

屬性 描述
BatteryStats 電量統計的接口
ApplicationInfo 系統進程的ApplicationInfo是從android包中解析出來的數據; 應用程序的ApplicationInfo是從AndroidManifest.xml中解析出來的數據
Process Name 進程名稱
UID 進程的UID。系統進程的UID是1000(Process.SYSTEM_UID); 應用進程的UID是從10000(Process.FIRST_APPLICATION_UID)開始分配的
maxAdj, curAdj, setAdj 各類不一樣的OOM Adjustment值
lastPss, lastPssTime 物理內存(PSS)相關,進程中有對象建立或銷燬時,PSS相關的屬性會被更新。
activities, services, receivers 進程中的Android組件,隨着進程的運行,這些信息均可能須要更新。譬如Activity的啓動時,ProcessRecord.activies會增長一個實例; 銷燬時,對將對應的實例從activities刪除
pkgList 進程中運行的包
thread 該屬性是IApplicationThread類型的對象

ProcessRecord有「激活(Active)」和「非激活(Inactive)」兩種狀態,只有將ProcessRecord綁定到一個實際進程的時候,纔是激活狀態。 綁定成功後,thread屬性就被賦值,表示ProcessRecord已經激活。 激活後,AMS就能夠經過這個接口完成對應用進程的管理,譬如啓動Activity、派發廣播等。 將ProcessRecord綁定到應用進程的過程在Android四大組件之Activity–應用進程與系統進程的通訊一文中有詳細的分析。

行爲 描述
makeActive() 將ProcessRecord置成激活狀態
makeInactive() 將ProcessRecord置成非激活狀態
addPackage() 向ProcessRecord添加包

2.2 關聯關係

Maintenace Guideline
  • AMS運行在SystemServer進程中。SystemServer進程啓動時,會經過SystemServer.startBootstrapServices()來建立一個AMS的對象;
private void startBootstrapServices() { ... mActivityManagerService = mSystemServiceManager.startService( ActivityManagerService.Lifecycle.class).getService(); ... }
  • AMS經過ActivityStackSupervisor來管理Activity。AMS對象只會存在一個,在初始化的時候,會建立一個惟一的ActivityStackSupervisor對象;
public ActivityManagerService(Context systemContext) { ... mStackSupervisor = new ActivityStackSupervisor(this); ... }
  • ActivityStackSupervisor中維護了顯示設備的信息。當有新的顯示設備添加時,會建立一個新的ActivityDisplay對象;
public void handleDisplayAddedLocked(int displayId) { ... newDisplay = mActivityDisplays.get(displayId) == null; if (newDisplay) { ActivityDisplay activityDisplay = new ActivityDisplay(displayId); ... } ... }
  • ActivityStack與顯示設備的綁定。當須要建立一個ActivityStack時,須要將其綁定到具體的顯示設備。 ActivityStackSupervisor經過ActivityContainer這個內部類對ActivityStack進行了一層封裝, 因此,會首先建立一個ActivityContainer對象;而後,經過ActivityContainer.attachToDisplayLocked()函數進行具體的綁定操做;
private int createStackOnDisplay(int stackId, int displayId) { ... ActivityContainer activityContainer = new ActivityContainer(stackId); mActivityContainers.put(stackId, activityContainer); activityContainer.attachToDisplayLocked(activityDisplay); return stackId; }
  • AMS維護了全部進程的信息ProcessRecord。當須要建立一個新的進程時, 會經過AMS.newProcessRecordLocked()函數來建立一個ProcessRecord對象, ProcessRecord對象都保存在AMS.mPidsSelfLocked這個屬性中;
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess, boolean isolated, int isolatedUid) { ... return new ProcessRecord(stats, info, proc, uid); }
  • 經過ActivityStackSupervisor來建立ActivityRecord。當SystemServer進程收到來自應用進程的啓動Activity請求時, 會經過ActivityStackSupervisor來建立一個ActivityRecord對象;
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); ... }
  • 在ActivityStack上建立TaskRecord。當須要建立新的任務棧時,就會經過ActivityStack對象來建立一個TaskRecord對象, 這樣就創建了ActivityStack和TaskRecord的關聯;
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; }
  • ActivityRecord的宿主TaskRecord。每個ActivityRecord都須要找到本身的宿主TaskRecord,經過ActivityRecord.setTask()函數 就能創建ActivityRecord和TaskRecord的關聯;
void setTask(TaskRecord newTask, TaskRecord taskToAffiliateWith) { ... task = newTask; setTaskToAffiliateWith(taskToAffiliateWith); }
  • 進程中運行的Activity信息。Activity在應用進程中運行,AMS中記錄了進程中全部運行的Activity的信息,在ActivityRecord建立後, 會經過ProcessRecord.addPackage()函數,在ProcessRecord中登記ActivityRecord的信息
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, ... }

3. Activity管理的延伸

在分析完Activity管理的基礎數據結構及關聯關係後,想必各位讀者已經感覺到了Activity管理的複雜性。 如此龐大而精密的數據結構設計,是在什麼背景下產生的呢?從已有的Activity設計中, 可否窺探出之後Android在Activity相關特性的發展方向呢?譬如多屏幕、多窗口的Activity顯示。

筆者一直認爲,研究Android源碼不只僅是理解Android的內部運行機制,更重要的是體會出其背後的設計思想, 總結出一套解決同類問題的方法論,而後再到具體的軟件開發中進行實踐,哪怕不在Android平臺下開發, 提煉出來的方法論仍然是受用的。

相關文章
相關標籤/搜索