ActivityTask 和 Activity 棧

ActivityStack

ActivityStack 是一個管理類粮呢,用來管理系統(tǒng)所有 Activity 的各種狀態(tài)茧彤,其內(nèi)部維護了 TaskRecord 的列表屎勘,因此從 Activity 任務棧這一角度來說示启,ActivityStack 也可以理解為 Activity 堆棧湃密。

它由 ActivityStackSupervisor 來進行管理的巫员,而 ActivityStackSupervisor 在 AMS 中的構造方法中被創(chuàng)建庶香。

image

ActivityStack 的實例類型

<pre class="md-fences md-end-block" lang="java" contenteditable="false" cid="n9" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: monospace, monospace; font-size: 0.9rem; white-space: pre; line-height: 1.71429em; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(218, 218, 218); position: relative !important; margin-bottom: 3em; padding-left: 1ch; padding-right: 1ch; margin-left: 2em; width: inherit; color: rgb(31, 9, 9); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">//com.android.server.am.ActivityStackSupervisor
public final class ActivityStackSupervisor implements DisplayListener {
...
ActivityStack mHomeStack;//存儲 Launcher App 的所有 Activity
ActivityStack mFocusedStack;//當前正在接收輸入或啟動下一個 Activity 的所有 Activity
private ActivityStack mLastFocusedStack;//此前接收過輸入的所有 Activity
...
}</pre>

ActivityRecord

ActivityRecord 用來記錄一個 Activity 的所有信息。從 Activity 任務棧的角度來說简识,一個或多個 ActivityRecord 會組成一個 TaskRecord赶掖,TaskRecord 用來記錄 Activity 的棧,而 ActivityStack 包含了一個或多個 TaskRecord七扰。

<pre class="md-fences md-end-block" lang="java" contenteditable="false" cid="n13" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: monospace, monospace; font-size: 0.9rem; white-space: pre; line-height: 1.71429em; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(218, 218, 218); position: relative !important; margin-bottom: 3em; padding-left: 1ch; padding-right: 1ch; margin-left: 2em; width: inherit; color: rgb(31, 9, 9); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">
//在 ActivityStack 中定義了一些特殊狀態(tài)的 Activity
ActivityRecord mPausingActivity = null;//正在暫停的Activity
ActivityRecord mLastPausedActivity = null;//上一個已經(jīng)暫停的Activity
ActivityRecord mLastNoHistoryActivity = null;//最近一次沒有歷史記錄的Activity
ActivityRecord mResumedActivity = null;//已經(jīng)Resume的Activity
ActivityRecord mLastStartedActivity = null;//最近一次啟動的Activity
ActivityRecord mTranslucentActivityWaiting = null;//傳遞給convertToTranslucent方法的最上層的Activity</pre>

ActivityState

<pre class="md-fences md-end-block" lang="java" contenteditable="false" cid="n15" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: monospace, monospace; font-size: 0.9rem; white-space: pre; line-height: 1.71429em; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(218, 218, 218); position: relative !important; margin-bottom: 3em; padding-left: 1ch; padding-right: 1ch; margin-left: 2em; width: inherit; color: rgb(31, 9, 9); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">
//通過枚舉存儲了 Activity 的所有的9種狀態(tài)
enum ActivityState {
INITIALIZING,
RESUMED,
PAUSING,
PAUSED,
STOPPING,
STOPPED,
FINISHING,
DESTROYING,
DESTROYED
}</pre>

維護的 ArrayList

ActivityStack 中維護了很多 ArrayList奢赂,這些 ArrayList 中的元素類型主要有 ActivityRecord 和 TaskRecord,其中 TaskRecord 用來記錄 Activity 的 Task颈走。

ARRAYLIST 元素類型 說明
mTaskHistory TaskRecord 所有沒有被銷毀的 Task
mLRUActivities ActivityRecord 正在運行的 Activity膳灶,列表中的第一個條目是最近最少使用的元素
mNoAnimActivities ActivityRecord 不考慮轉(zhuǎn)換動畫的 Activity
mValidateAppTokens TaskGroup 用于與窗口管理器驗證應用令牌

Activity 棧管理

Activity 是由任務棧來進行管理的,有了棧管理立由,我們可以對應用程序進行操作轧钓,應用可以復用自身應用中以及其他應用的 Activity,節(jié)省了資源锐膜。

比如我們使用一款社交應用毕箍,這個應用的聯(lián)系人詳情界面提供了聯(lián)系人的郵箱,當我們點擊郵箱時會跳到發(fā)送郵件的界面道盏。社交應用和系統(tǒng) Email 中的 Activity 是處于不同應用程序進程的而柑,而有了棧管理,就可以把發(fā)送郵件界面放到社交應用中詳情界面所在棧的棧頂荷逞,來做到跨進程操作媒咳。

image

Launch Mode

Launch Mode用于設定 Activity 的啟動方式,無論是哪種啟動方式种远,所啟動的 Activity 都會位于 Activity 棧的棧頂涩澡。有以下四種:

  • standerd:默認模式,每次啟動 Activity 都會創(chuàng)建一個新的 Activity 實例院促。

  • singleTop:如果要啟動的 Activity 已經(jīng)在棧頂筏养,則不會重新創(chuàng)建 Activity,同時該 Activity 的 onNewIntent 方法會被調(diào)用常拓。如果要啟動的 Activity 不在棧頂渐溶,則會重新創(chuàng)建該 Activity 的實例。

  • singleTask:如果要啟動的 Activity 已經(jīng)存在于它想要歸屬的棧中弄抬,那么不會創(chuàng)建該 Activity 實例茎辐,將棧中位于該 Activity 上的所有的 Activity 出棧浅缸,同時該 Activity 的 onNewIntent 方法會被調(diào)用。如果要啟動的 Activity 不存在于它想要歸屬的棧中庇绽,并且該棧存在教届,則會創(chuàng)建該 Activity 的新實例。如果要啟動的 Activity 想要歸屬的棧不存在依啰,則首先要創(chuàng)建一個新棧乎串,然后創(chuàng)建該 Activity 實例并壓入到新棧中。

  • singleInstance:啟動 Activity 時速警,首先要創(chuàng)建在一個新棧叹誉,然后創(chuàng)建該 Activity 實例并壓入新棧中,新棧中只會存在這一個 Activity 實例闷旧。

Intent 的 FLAG

Intent 中定義了很多了 FLAG长豁,其中有幾個 FLAG 也可以設定 Activity 的啟動方式,如果 Launch Mode 設定和 FLAG 設定的 Activity 的啟動方式有沖突忙灼,則以 FLAG 設定的為準匠襟。

  • FLAG_ACTIVITY_SINGLE_TOP:和 Launch Mode 中的 singleTop 效果是一樣的。

  • FLAG_ACTIVITY_NEW_TASK:和 Launch Mode 中的 singleTask 效果是一樣的该园。這個名字比較奇怪要注意了酸舍。

  • FLAG_ACTIVITY_CLEAR_TOP:Launch Mode 中沒有與此對應的模式,如果要啟動的 Activity 已經(jīng)存在于棧中爬范,則將所有位于它上面的 Activity 出棧父腕。singleTask 默認具有此標記位的效果。

除了這三個 FLAG青瀑,還有一些 FLAG 對我們分析棧管理有些幫助璧亮。

  • FLAG_ACTIVITY_NO_HISTORY:Activity 一旦退出,就不會存在于棧中斥难。同樣的枝嘶,也可以在 AndroidManifest.xml 中設置 “android:noHistory”。

  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:Activity 不會被放入到 “最近啟動的 Activity” 列表中哑诊。

  • FLAG_ACTIVITY_BROUGHT_TO_FRONT:這個標志位通常不是由應用程序中的代碼設置的群扶,而是 Launch Mode 為 singleTask 時,由系統(tǒng)自動加上的镀裤。

  • FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY:這個標志位通常不是由應用程序中的代碼設置的竞阐,而是從歷史記錄中啟動的(長按 Home 鍵調(diào)出)。

  • FLAG_ACTIVITY_MULTIPLE_TASK:需要和 FLAG_ACTIVITY_NEW_TASK 一同使用才有效果暑劝,系統(tǒng)會啟動一個新的棧來容納新啟動的 Activity.

  • FLAG_ACTIVITY_CLEAR_TASK:需要和 FLAG_ACTIVITY_NEW_TASK 一同使用才有效果骆莹,用于清除與啟動的 Activity 相關棧的所有其他 Activity。

計算Flag

根 Activity 啟動時會調(diào)用 AMS 的 startActivity 方法担猛,經(jīng)過層層調(diào)用會調(diào)用 ActivityStarter 的 startActivityUnchecked 方法

<pre class="md-fences md-end-block" lang="java" contenteditable="false" cid="n100" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: monospace, monospace; font-size: 0.9rem; white-space: pre; line-height: 1.71429em; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(218, 218, 218); position: relative !important; margin-bottom: 3em; padding-left: 1ch; padding-right: 1ch; margin-left: 2em; width: inherit; color: rgb(31, 9, 9); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">
//com.android.server.am.ActivityStarter#startActivityUnchecked
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);//重置各種配置再進行配置幕垦,如:ActivityRecord丢氢、Intent、TaskRecord 和 LaunchFlags(啟動的 FLAG)等
computeLaunchingTaskFlags();//計算出啟動的 FLAG先改,并將計算的值賦值給 mLaunchFlags
computeSourceStack();
mIntent.setFlags(mLaunchFlags);//將 mLaunchFlags 設置給 Intent疚察,達到設定 Activity 的啟動方式的目的
...
}
//com.android.server.am.ActivityStarter#computeLaunchingTaskFlags
private void computeLaunchingTaskFlags() {
...
if (mInTask == null) {// TaskRecord 類型的 mInTask 為 null 時,說明 Activity 要加入的棧不存在
if (mSourceRecord == null) {//初始 Activity(ActivityA 啟動了 ActivityB仇奶,ActivityA 就是初始 Activity)為null
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {//需要創(chuàng)建一個新棧
Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
"Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
} else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {//如果 "初始 Activity" 所在的棧只允許有一個 Activity 實例貌嫡,則需要創(chuàng)建一個新棧
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
} else if (mLaunchSingleInstance || mLaunchSingleTask) {//Launch Mode 設置了 singleTask 或 singleInstance,則也要創(chuàng)建一個新棧
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
}
}</pre>

taskAffinity

任務的親近性猜嘱,可以在 AndroidManifest.xml 設置 android:taskAffinity衅枫,用來指定 Activity 希望歸屬的棧嫁艇, 默認情況下朗伶,同一個應用程序的所有的 Activity 都有著相同的 taskAffinity。

taskAffinity 在下面兩種情況時會產(chǎn)生效果步咪。

  1. taskAffinity 與 FLAG_ACTIVITY_NEW_TASK 或者 singleTask 配合论皆。如果新啟動 Activity 的 taskAffinity 和棧的 taskAffinity 相同(棧的 taskAffinity 取決于根 Activity 的 taskAffinity)則加入到該棧中。如果不同猾漫,就會創(chuàng)建新棧点晴。

  2. taskAffinity 與 allowTaskReparenting 配合。如果 allowTaskReparenting 為 true悯周,說明 Activity 具有轉(zhuǎn)移的能力粒督。拿此前的郵件為例,當社交應用啟動了發(fā)送郵件的 Activity禽翼,此時發(fā)送郵件的 Activity 是和社交應用處于同一個棧中屠橄。如果發(fā)送郵件的 Activity 的 allowTaskReparenting 設置為 true,此后郵件程序所在的棧位于前臺闰挡,這個時候發(fā)送郵件的 Activity 就會由社交應用的棧中轉(zhuǎn)移到與它更親近的郵件程序(taskAffinity 相同)所在的棧中锐墙。

taskAffinity的計算

<pre class="md-fences md-end-block" lang="java" contenteditable="false" cid="n114" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: monospace, monospace; font-size: 0.9rem; white-space: pre; line-height: 1.71429em; display: block; break-inside: avoid; text-align: left; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(218, 218, 218); position: relative !important; margin-bottom: 3em; padding-left: 1ch; padding-right: 1ch; margin-left: 2em; width: inherit; color: rgb(31, 9, 9); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">
//com.android.server.am.ActivityStack#findTaskLocked
void findTaskLocked(ActivityRecord target, FindTaskResult result) {
...
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {//遍歷 mTaskHistory 列表,列表的元素為 TaskRecord长酗,
用于存儲沒有被銷毀的 Task
final TaskRecord task = mTaskHistory.get(taskNdx);//得到某一個 Task 的信息
...
else if (!isDocument && !taskIsDocument
&& result.r == null && task.canMatchRootAffinity()) {
if (task.rootAffinity.equals(target.taskAffinity)) {//將 Task 的 rootAffinity(初始的 taskAffinity)和目標 Activity 的 taskAffinity 做對比溪北,如果相同,則將 FindTaskResult 的 matchedByRootAffinity 屬性設置為 true夺脾,說明找到了匹配的 Task
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching affinity candidate!");
result.r = r;
result.matchedByRootAffinity = true;
}
} else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
}
}</pre>

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末之拨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子咧叭,更是在濱河造成了極大的恐慌蚀乔,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件佳簸,死亡現(xiàn)場離奇詭異乙墙,居然都是意外死亡颖变,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門听想,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腥刹,“玉大人,你說我怎么就攤上這事汉买∠畏澹” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵蛙粘,是天一觀的道長垫卤。 經(jīng)常有香客問我,道長出牧,這世上最難降的妖魔是什么穴肘? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮舔痕,結果婚禮上评抚,老公的妹妹穿的比我還像新娘。我一直安慰自己伯复,他們只是感情好慨代,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著啸如,像睡著了一般侍匙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上叮雳,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天想暗,我揣著相機與錄音,去河邊找鬼债鸡。 笑死江滨,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的厌均。 我是一名探鬼主播唬滑,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼棺弊!你這毒婦竟也來了晶密?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤模她,失蹤者是張志新(化名)和其女友劉穎稻艰,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侈净,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡尊勿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年僧凤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片元扔。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡躯保,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出澎语,到底是詐尸還是另有隱情途事,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布擅羞,位于F島的核電站尸变,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏减俏。R本人自食惡果不足惜召烂,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望垄懂。 院中可真熱鬧骑晶,春花似錦、人聲如沸草慧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漫谷。三九已至,卻和暖如春蹂析,著一層夾襖步出監(jiān)牢的瞬間舔示,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工电抚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留惕稻,地道東北人。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓蝙叛,卻偏偏與公主長得像俺祠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子借帘,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容