四大組件之Activity_任務和返回棧

[文章內(nèi)容來自Developers]
應用通常包含多個 Activity。每個 Activity 均應圍繞用戶可以執(zhí)行的特定操作設計,并且能夠啟動其他 Activity。 例如,電子郵件應用可能有一個 Activity 顯示新郵件的列表。用戶選擇某郵件時,會打開一個新 Activity 以查看該郵件。
一個 Activity 甚至可以啟動設備上其他應用中存在的 Activity。例如,如果應用想要發(fā)送電子郵件,則可將 Intent 定義為執(zhí)行“發(fā)送”操作并加入一些數(shù)據(jù),如電子郵件地址和電子郵件。 然后,系統(tǒng)將打開其他應用中聲明自己處理此類 Intent 的 Activity。在這種情況下,Intent 是要發(fā)送電子郵件,因此將啟動電子郵件應用的“撰寫”Activity(如果多個 Activity 支持相同 Intent,則系統(tǒng)會讓用戶選擇要使用的 Activity)。發(fā)送電子郵件時,Activity 將恢復,看起來好像電子郵件 Activity 是您的應用的一部分。 即使這兩個 Activity 可能來自不同的應用,但是 Android 仍會將 Activity 保留在相同的任務中,以維護這種無縫的用戶體驗。
任務是指在執(zhí)行特定作業(yè)時與用戶交互的一系列 Activity。 這些 Activity 按照各自的打開順序排列在堆棧(即返回棧)中。
設備主屏幕是大多數(shù)任務的起點。當用戶觸摸應用啟動器中的圖標(或主屏幕上的快捷方式)時,該應用的任務將出現(xiàn)在前臺。 如果應用不存在任務(應用最近未曾使用),則會創(chuàng)建一個新任務,并且該應用的“主”Activity 將作為堆棧中的根 Activity 打開。
當前 Activity 啟動另一個 Activity 時,該新 Activity 會被推送到堆棧頂部,成為焦點所在。 前一個 Activity 仍保留在堆棧中,但是處于停止狀態(tài)。Activity 停止時,系統(tǒng)會保持其用戶界面的當前狀態(tài)。 用戶按“返回”按鈕時,當前 Activity 會從堆棧頂部彈出(Activity 被銷毀),而前一個 Activity 恢復執(zhí)行(恢復其 UI 的前一狀態(tài))。 堆棧中的 Activity 永遠不會重新排列,僅推入和彈出堆棧:由當前 Activity 啟動時推入堆棧;用戶使用“返回”按鈕退出時彈出堆棧。 因此,返回棧以“后進先出”對象結(jié)構(gòu)運行。 圖 1 通過時間線顯示 Activity 之間的進度以及每個時間點的當前返回棧,直觀呈現(xiàn)了這種行為。

圖 1. 顯示任務中的每個新 Activity 如何向返回棧添加項目。 用戶按“返回”按鈕時,當前 Activity 隨即被銷毀,而前一個 Activity 恢復執(zhí)行。
如果用戶繼續(xù)按“返回”,堆棧中的相應 Activity 就會彈出,以顯示前一個 Activity,直到用戶返回主屏幕為止(或者,返回任務開始時正在運行的任意 Activity)。 當所有 Activity 均從堆棧中移除后,任務即不復存在。
圖 2. 兩個任務:任務 B 在前臺接收用戶交互,而任務 A 則在后臺等待恢復。

圖 3. 一個 Activity 將多次實例化。

任務是一個有機整體,當用戶開始新任務或通過“主頁”按鈕轉(zhuǎn)到主屏幕時,可以移動到“后臺”。 盡管在后臺時,該任務中的所有 Activity 全部停止,但是任務的返回棧仍舊不變,也就是說,當另一個任務發(fā)生時,該任務僅僅失去焦點而已,如圖 2 中所示。然后,任務可以返回到“前臺”,用戶就能夠回到離開時的狀態(tài)。 例如,假設當前任務(任務 A)的堆棧中有三個 Activity,即當前 Activity 下方還有兩個 Activity。 用戶先按“主頁”按鈕,然后從應用啟動器啟動新應用。 顯示主屏幕時,任務 A 進入后臺。新應用啟動時,系統(tǒng)會使用自己的 Activity 堆棧為該應用啟動一個任務(任務 B)。與該應用交互之后,用戶再次返回主屏幕并選擇最初啟動任務 A 的應用?,F(xiàn)在,任務 A 出現(xiàn)在前臺,其堆棧中的所有三個 Activity 保持不變,而位于堆棧頂部的 Activity 則會恢復執(zhí)行。 此時,用戶還可以通過轉(zhuǎn)到主屏幕并選擇啟動該任務的應用圖標(或者,通過從概覽屏幕選擇該應用的任務)切換回任務 B。這是 Android 系統(tǒng)中的一個多任務示例。

:后臺可以同時運行多個任務。但是,如果用戶同時運行多個后臺任務,則系統(tǒng)可能會開始銷毀后臺 Activity,以回收內(nèi)存資源,從而導致 Activity 狀態(tài)丟失。請參閱下面有關Activity 狀態(tài)的部分。

由于返回棧中的 Activity 永遠不會重新排列,因此如果應用允許用戶從多個 Activity 中啟動特定 Activity,則會創(chuàng)建該 Activity 的新實例并推入堆棧中(而不是將 Activity 的任一先前實例置于頂部)。 因此,應用中的一個 Activity 可能會多次實例化(即使 Activity 來自不同的任務),如圖 3 所示。因此,如果用戶使用“返回”按鈕向后導航,則會按 Activity 每個實例的打開順序顯示這些實例(每個實例的 UI 狀態(tài)各不相同)。 但是,如果您不希望 Activity 多次實例化,則可修改此行為。 具體操作方法將在后面的管理任務部分中討論。
Activity 和任務的默認行為總結(jié)如下:

  • 當 Activity A 啟動 Activity B 時,Activity A 將會停止,但系統(tǒng)會保留其狀態(tài)(例如,滾動位置和已輸入表單中的文本)。如果用戶在處于 Activity B 時按“返回”按鈕,則 Activity A 將恢復其狀態(tài),繼續(xù)執(zhí)行。
  • 用戶通過按“主頁”按鈕離開任務時,當前 Activity 將停止且其任務會進入后臺。 系統(tǒng)將保留任務中每個 Activity 的狀態(tài)。如果用戶稍后通過選擇開始任務的啟動器圖標來恢復任務,則任務將出現(xiàn)在前臺并恢復執(zhí)行堆棧頂部的 Activity。
    如果用戶按“返回”按鈕,則當前 Activity 會從堆棧彈出并被銷毀。 堆棧中的前一個 Activity 恢復執(zhí)行。銷毀 Activity 時,系統(tǒng)不會保留該 Activity 的狀態(tài)。
  • 即使來自其他任務,Activity 也可以多次實例化。

導航設計
如需了解有關 Android 應用導航工作方式的詳細信息,請閱讀 Android 設計的導航指南。

保存 Activity 狀態(tài)


正如上文所述,當 Activity 停止時,系統(tǒng)的默認行為會保留其狀態(tài)。 這樣一來,當用戶導航回到上一個 Activity 時,其用戶界面與用戶離開時一樣。 但是,在 Activity 被銷毀且必須重建時,您可以而且應當主動使用回調(diào)方法保留 Activity 的狀態(tài)。
系統(tǒng)停止您的一個 Activity 時(例如,新 Activity 啟動或任務轉(zhuǎn)到前臺),如果系統(tǒng)需要回收系統(tǒng)內(nèi)存資源,則可能會完全銷毀該 Activity。 發(fā)生這種情況時,有關該 Activity 狀態(tài)的信息將會丟失。如果發(fā)生這種情況,系統(tǒng)仍會知道該 Activity 存在于返回棧中,但是當該 Activity 被置于堆棧頂部時,系統(tǒng)一定會重建 Activity(而不是恢復 Activity)。 為了避免用戶的工作丟失,您應主動通過在 Activity 中實現(xiàn)onSaveInstanceState()回調(diào)方法來保留工作。

管理任務


Android 管理任務和返回棧的方式(如上所述,即:將所有連續(xù)啟動的 Activity 放入同一任務和“后進先出”堆棧中)非常適用于大多數(shù)應用,而您不必擔心 Activity 如何與任務關聯(lián)或者如何存在于返回棧中。 但是,您可能會決定要中斷正常行為。 也許您希望應用中的 Activity 在啟動時開始新任務(而不是放置在當前任務中);或者,當啟動 Activity 時,您希望將其現(xiàn)有實例上移一層(而不是在返回棧的頂部創(chuàng)建新實例);或者,您希望在用戶離開任務時,清除返回棧中除根 Activity 以外的所有其他 Activity。
通過使用 <activity>清單文件元素中的屬性和傳遞給 startActivity()的 Intent 中的標志,您可以執(zhí)行所有這些操作以及其他操作。
在這一方面,您可以使用的主要 <activity>屬性包括:
taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch

您可以使用的主要 Intent 標志包括:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_SINGLE_TOP

在下文中,您將了解如何使用這些清單文件屬性和 Intent 標志定義 Activity 與任務的關聯(lián)方式,以及 Activity 在返回棧中的行為方式。
此外,我們還單獨介紹了有關如何在概覽屏幕中顯示和管理任務與 Activity 的注意事項。 通常,您應該允許系統(tǒng)定義任務和 Activity 在概覽屏幕中的顯示方法,并且無需修改此行為。

注意:大多數(shù)應用都不得中斷 Activity 和任務的默認行為: 如果確定您的 Activity 必須修改默認行為,當使用“返回”按鈕從其他 Activity 和任務導航回到該 Activity 時,請務必要謹慎并確保在啟動期間測試該 Activity 的可用性。請確保測試導航行為是否有可能與用戶的預期行為沖突。

定義啟動模式
啟動模式允許您定義 Activity 的新實例如何與當前任務關聯(lián)。 您可以通過兩種方法定義不同的啟動模式:
使用清單文件在清單文件中聲明 Activity 時,您可以指定 Activity 在啟動時應該如何與任務關聯(lián)。

使用 Intent 標志調(diào)用 startActivity()時,可以在 Intent中加入一個標志,用于聲明新 Activity 如何(或是否)與當前任務關聯(lián)。

因此,如果 Activity A 啟動 Activity B,則 Activity B 可以在其清單文件中定義它應該如何與當前任務關聯(lián)(如果可能),并且 Activity A 還可以請求 Activity B 應該如何與當前任務關聯(lián)。如果這兩個 Activity 均定義 Activity B 應該如何與任務關聯(lián),則 Activity A 的請求(如 Intent 中所定義)優(yōu)先級要高于 Activity B 的請求(如其清單文件中所定義)。

:某些適用于清單文件的啟動模式不可用作 Intent 標志,同樣,某些可用作 Intent 標志的啟動模式無法在清單文件中定義。

使用清單文件
在清單文件中聲明 Activity 時,您可以使用 <activity>元素的 launchMode屬性指定 Activity 應該如何與任務關聯(lián)。
launchMode屬性指定有關應如何將 Activity 啟動到任務中的指令。您可以分配給 launchMode屬性的啟動模式共有四種:

  • "standard"(默認模式)
    默認。系統(tǒng)在啟動 Activity 的任務中創(chuàng)建 Activity 的新實例并向其傳送 Intent。Activity 可以多次實例化,而每個實例均可屬于不同的任務,并且一個任務可以擁有多個實例。
  • "singleTop"
    如果當前任務的頂部已存在 Activity 的一個實例,則系統(tǒng)會通過調(diào)用該實例的 onNewIntent()方法向其傳送 Intent,而不是創(chuàng)建 Activity 的新實例。Activity 可以多次實例化,而每個實例均可屬于不同的任務,并且一個任務可以擁有多個實例(但前提是位于返回棧頂部的 Activity 并不是 Activity 的現(xiàn)有實例)。**例如,假設任務的返回棧包含根 Activity A 以及 Activity B、C 和位于頂部的 D(堆棧是 A-B-C-D;D 位于頂部)。收到針對 D 類 Activity 的 Intent。如果 D 具有默認的 "standard"
    啟動模式,則會啟動該類的新實例,且堆棧會變成 A-B-C-D-D。但是,如果 D 的啟動模式是 "singleTop",則 D 的現(xiàn)有實例會通過 onNewIntent() 接收 Intent,因為它位于堆棧的頂部;而堆棧仍為 A-B-C-D。但是,如果收到針對 B 類 Activity 的 Intent,則會向堆棧添加 B 的新實例,即便其啟動模式為 "singleTop"也是如此。

:為某個 Activity 創(chuàng)建新實例時,用戶可以按“返回”按鈕返回到前一個 Activity。 但是,當 Activity 的現(xiàn)有實例處理新 Intent 時,則在新 Intent 到達 onNewIntent()
之前,用戶無法按“返回”按鈕返回到 Activity 的狀態(tài)。

  • "singleTask"
    系統(tǒng)創(chuàng)建新任務并實例化位于新任務底部的 Activity。但是,如果該 Activity 的一個實例已存在于一個單獨的任務中,則系統(tǒng)會通過調(diào)用現(xiàn)有實例的 onNewIntent()方法向其傳送 Intent,而不是創(chuàng)建新實例。一次只能存在 Activity 的一個實例。:盡管 Activity 在新任務中啟動,但是用戶按“返回”按鈕仍會返回到前一個 Activity。
  • "singleInstance".
    與 "singleTask"相同,只是系統(tǒng)不會將任何其他 Activity 啟動到包含實例的任務中。該 Activity 始終是其任務唯一僅有的成員;由此 Activity 啟動的任何 Activity 均在單獨的任務中打開。

我們再來看另一示例,Android 瀏覽器應用聲明網(wǎng)絡瀏覽器 Activity 應始終在其自己的任務中打開(通過在 <activity>元素中指定 singleTask啟動模式)。這意味著,如果您的應用發(fā)出打開 Android 瀏覽器的 Intent,則其 Activity 與您的應用位于不同的任務中。相反,系統(tǒng)會為瀏覽器啟動新任務,或者如果瀏覽器已有任務正在后臺運行,則會將該任務上移一層以處理新 Intent。
無論 Activity 是在新任務中啟動,還是在與啟動 Activity 相同的任務中啟動,用戶按“返回”按鈕始終會轉(zhuǎn)到前一個 Activity。 但是,如果啟動指定singleTask啟動模式的 Activity,則當某后臺任務中存在該 Activity 的實例時,整個任務都會轉(zhuǎn)移到前臺。此時,返回棧包括上移到堆棧頂部的任務中的所有 Activity。 圖 4 顯示了這種情況。

圖 4. 顯示如何將啟動模式為“singleTask”的 Activity 添加到返回棧。 如果 Activity 已經(jīng)是某個擁有自己的返回棧的后臺任務的一部分,則整個返回棧也會上移到當前任務的頂部。

如需了解有關在清單文件中使用啟動模式的詳細信息,請參閱 <activity>元素文檔,其中更詳細地討論了 launchMode
屬性和可接受的值。

:使用 launchMode屬性為 Activity 指定的行為可由 Intent 附帶的 Activity 啟動標志替代,下文將對此進行討論。

使用 Intent 標志
啟動 Activity 時,您可以通過在傳遞給 startActivity()的 Intent 中加入相應的標志,修改 Activity 與其任務的默認關聯(lián)方式??捎糜谛薷哪J行為的標志包括:

FLAG_ACTIVITY_NEW_TASK

在新任務中啟動 Activity。如果已為正在啟動的 Activity 運行任務,則該任務會轉(zhuǎn)到前臺并恢復其最后狀態(tài),同時 Activity 會在onNewIntent()中收到新 Intent。正如前文所述,這會產(chǎn)生與 "singleTask"launchMode值相同的行為。

FLAG_ACTIVITY_SINGLE_TOP

如果正在啟動的 Activity 是當前 Activity(位于返回棧的頂部),則 現(xiàn)有實例會接收對 onNewIntent()的調(diào)用,而不是創(chuàng)建 Activity 的新實例。正如前文所述,這會產(chǎn)生與 "singleTop"
launchMode值相同的行為。

FLAG_ACTIVITY_CLEAR_TOP

如果正在啟動的 Activity 已在當前任務中運行,則會銷毀當前任務頂部的所有 Activity,并通過 onNewIntent()將此 Intent 傳遞給 Activity 已恢復的實例(現(xiàn)在位于頂部),而不是啟動該 Activity 的新實例。產(chǎn)生這種行為的 launchMode 屬性沒有值。

FLAG_ACTIVITY_CLEAR_TOP通常與 FLAG_ACTIVITY_NEW_TASK結(jié)合使用。一起使用時,通過這些標志,可以找到其他任務中的現(xiàn)有 Activity,并將其放入可從中響應 Intent 的位置。

:如果指定 Activity 的啟動模式為 "standard"
,則該 Activity 也會從堆棧中移除,并在其位置啟動一個新實例,以便處理傳入的 Intent。 這是因為當啟動模式為 "standard"
時,將始終為新 Intent 創(chuàng)建新實例。

處理關聯(lián)
“關聯(lián)”指示 Activity 優(yōu)先屬于哪個任務。默認情況下,同一應用中的所有 Activity 彼此關聯(lián)。 因此,默認情況下,同一應用中的所有 Activity 優(yōu)先位于相同任務中。 不過,您可以修改 Activity 的默認關聯(lián)。 在不同應用中定義的 Activity 可以共享關聯(lián),或者可為在同一應用中定義的 Activity 分配不同的任務關聯(lián)。
可以使用 <activity>元素的 taskAffinity屬性修改任何給定 Activity 的關聯(lián)。
taskAffinity屬性取字符串值,該值必須不同于在[<manifest>元素中聲明的默認軟件包名稱,因為系統(tǒng)使用該名稱標識應用的默認任務關聯(lián)。
在兩種情況下,關聯(lián)會起作用:

  • 啟動 Activity 的 Intent 包含 FLAG_ACTIVITY_NEW_TASK標志。默認情況下,新 Activity 會啟動到調(diào)用 startActivity()的 Activity 任務中。它將推入與調(diào)用方相同的返回棧。 但是,如果傳遞給startActivity()的 Intent 包含 FLAG_ACTIVITY_NEW_TASK 標志,則系統(tǒng)會尋找其他任務來儲存新 Activity。這通常是新任務,但未做強制要求。 如果現(xiàn)有任務與新 Activity 具有相同關聯(lián),則會將 Activity 啟動到該任務中。 否則,將開始新任務。
    如果此標志導致 Activity 開始新任務,且用戶按“主頁”按鈕離開,則必須為用戶提供導航回任務的方式。 有些實體(如通知管理器)始終在外部任務中啟動 Activity,而從不作為其自身的一部分啟動 Activity,因此它們始終將 FLAG_ACTIVITY_NEW_TASK放入傳遞給startActivity()的 Intent 中。請注意,如果 Activity 能夠由可以使用此標志的外部實體調(diào)用,則用戶可以通過獨立方式返回到啟動的任務,例如,使用啟動器圖標(任務的根 Activity 具有 CATEGORY_LAUNCHER Intent 過濾器;)。

  • Activity 將其[allowTaskReparenting屬性設置為 "true"
    。在這種情況下,Activity 可以從其啟動的任務移動到與其具有關聯(lián)的任務(如果該任務出現(xiàn)在前臺)。
    例如,假設將報告所選城市天氣狀況的 Activity 定義為旅行應用的一部分。 它與同一應用中的其他 Activity 具有相同的關聯(lián)(默認應用關聯(lián)),并允許利用此屬性重定父級。當您的一個 Activity 啟動天氣預報 Activity 時,它最初所屬的任務與您的 Activity 相同。 但是,當旅行應用的任務出現(xiàn)在前臺時,系統(tǒng)會將天氣預報 Activity 重新分配給該任務并顯示在其中。

提示:如果從用戶的角度來看,一個 .apk
文件包含多個“應用”,則您可能需要使用 taskAffinity 屬性將不同關聯(lián)分配給與每個“應用”相關的 Activity。

清理返回棧
如果用戶長時間離開任務,則系統(tǒng)會清除所有 Activity 的任務,根 Activity 除外。 當用戶再次返回到任務時,僅恢復根 Activity。系統(tǒng)這樣做的原因是,經(jīng)過很長一段時間后,用戶可能已經(jīng)放棄之前執(zhí)行的操作,返回到任務是要開始執(zhí)行新的操作。
您可以使用下列幾個 Activity 屬性修改此行為:
alwaysRetainTaskState
如果在任務的根 Activity 中將此屬性設置為 "true",則不會發(fā)生剛才所述的默認行為。即使在很長一段時間后,任務仍將所有 Activity 保留在其堆棧中。
clearTaskOnLaunch
如果在任務的根 Activity 中將此屬性設置為 "true"
,則每當用戶離開任務然后返回時,系統(tǒng)都會將堆棧清除到只剩下根 Activity。 換而言之,它與 alwaysRetainTaskState正好相反。 即使只離開任務片刻時間,用戶也始終會返回到任務的初始狀態(tài)。
finishOnTaskLaunch
此屬性類似于 clearTaskOnLaunch,但它對單個 Activity 起作用,而非整個任務。 此外,它還有可能會導致任何 Activity 停止,包括根 Activity。 設置為 "true"時,Activity 仍是任務的一部分,但是僅限于當前會話。如果用戶離開然后返回任務,則任務將不復存在。

啟動任務
通過為 Activity 提供一個以 "android.intent.action.MAIN"
為指定操作、以 "android.intent.category.LAUNCHER"
為指定類別的 Intent 過濾器,您可以將 Activity 設置為任務的入口點。 例如:

<activity ... >    
<intent-filter ... >        
<action android:name="android.intent.action.MAIN" />        
<category android:name="android.intent.category.LAUNCHER" />    
</intent-filter>    
...
</activity>

此類 Intent 過濾器會使 Activity 的圖標和標簽顯示在應用啟動器中,讓用戶能夠啟動 Activity 并在啟動之后隨時返回到創(chuàng)建的任務中。
第二個功能非常重要:用戶必須能夠在離開任務后,再使用此 Activity 啟動器返回該任務。 因此,只有在 Activity 具有 ACTION_MAIN和 CATEGORY_LAUNCHER過濾器時,才應該使用將 Activity 標記為“始終啟動任務”的兩種啟動模式,即 "singleTask"和 "singleInstance"。例如,我們可以想像一下如果缺少過濾器會發(fā)生什么情況: Intent 啟動一個 "singleTask"
Activity,從而啟動一個新任務,并且用戶花了些時間處理該任務。然后,用戶按“主頁”按鈕。 任務現(xiàn)已發(fā)送到后臺,而且不可見?,F(xiàn)在,用戶無法返回到任務,因為該任務未顯示在應用啟動器中。
如果您并不想用戶能夠返回到 Activity,對于這些情況,請將 <activity>元素的 finishOnTaskLaunch設置為 "true"。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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

  • 任務和返回棧 應用通常包含多個Activity。每個 Activity 均應圍繞用戶可以執(zhí)行的特定操作設計,并且能...
    xinlingzhiyi閱讀 771評論 0 6
  • Activity 是一個應用組件,用戶可與其提供的屏幕進行交互,以執(zhí)行撥打電話、拍攝照片、發(fā)送電子郵件或查看地圖等...
    岳小川閱讀 546評論 0 3
  • 歡樂夏令營之舒蔚的思考 我的夏令營生活就這樣開始了,無聲無息來臨的學習生活每時每刻都在如信號般刺激著我的大腦。不知...
    林舒蔚閱讀 721評論 2 8
  • "要把某個東西設計得很好,你就必須熟悉它,真正了解它到底是什么。你需要持續(xù)投入很大的熱情才能真正徹底地明白它,你需...
    智行合義閱讀 1,909評論 0 51
  • 最近看了這本好久以前就想看的書——塵埃落定,書上標著普通高中語文課程標準指定書目。慚愧,高中的我并沒有看過這本書。...
    change_xu閱讀 1,013評論 0 0

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