工作與返回堆疊

「工作」是使用者嘗試在應用程式中執行某些操作時,可進行互動的一系列活動。這些活動會按照開啟順序排列在返回堆疊中。

舉例來說,電子郵件應用程式可能會有一個顯示新郵件清單的活動。使用者選取訊息時,系統會開啟新的活動來查看該訊息。這個新活動會新增至返回堆疊。接著,當使用者輕觸或手勢返回時,該新活動就會完成並從堆疊中彈出。

工作及其返回堆疊的生命週期

裝置主畫面是大多數工作的起點。當使用者在應用程式啟動器或主畫面中輕觸應用程式或捷徑的圖示時,該應用程式的工作就會移至前景。如果應用程式沒有任何工作,系統會建立新工作,並開啟該應用程式的主要活動,做為堆疊中的根活動。

當目前的活動啟動另一個活動時,新活動會推送至堆疊頂端,並取得焦點。先前的活動會保留在堆疊中,但會停止。活動停止時,系統會保留使用者介面的目前狀態。使用者執行返回動作時,系統會從堆疊頂端彈出目前活動並刪除。先前的活動會繼續執行,並還原先前的 UI 狀態。

堆疊中的活動一律不會重新排列,只會在啟動時推送,並在使用者透過返回按鈕或手勢關閉時彈出。因此,返回堆疊會以「後進先出」結構運作,如圖 1 所示。

圖 1. 工作中的每個新活動如何將項目新增至返回堆疊。當使用者輕觸或手勢返回時,系統會刪除目前的活動,並繼續執行上一個活動。

使用者繼續輕觸或手勢返回時,堆疊中的每個活動都會彈出,顯示前一個活動,直到使用者返回主畫面或工作開始時執行的活動。當所有活動都從堆疊中移除後,工作就不會再存在。

根啟動器活動的返回輕觸行為

根啟動器活動是指宣告意圖篩選器的活動,其中包含 ACTION_MAIN 和 CATEGORY_LAUNCHER。這些活動很獨特,因為它們是從應用程式啟動器進入應用程式的進入點,並用於啟動工作。

當使用者從根啟動器活動輕觸或手勢返回時,系統會根據裝置執行的 Android 版本,以不同方式處理事件。

Android 11 以下版本的系統行為
系統會完成活動。
Android 12 以上版本的系統行為

系統會將活動及其任務移至背景,而不是結束活動。這項行為與使用主畫面按鈕或手勢離開應用程式時的預設系統行為一致。

在大多數情況下,這項行為代表使用者可以更快從暖啟動狀態恢復應用程式,而不必從冷啟動狀態完全重新啟動應用程式。

如需提供自訂返回瀏覽,建議使用 AndroidX Activity API,而非覆寫 onBackPressed()。如果沒有任何元件攔截系統返回輕觸動作,AndroidX Activity API 會自動遵循適當的系統行為。

不過,如果應用程式覆寫 onBackPressed() 來處理返回瀏覽並完成活動,請更新實作方式,改為呼叫 super.onBackPressed(),而非完成活動。呼叫 super.onBackPressed() 會在適當情況下將活動及其任務移至背景,並為使用者提供更一致的跨應用程式導覽體驗。

背景和前景任務

圖 2. 兩項任務:任務 B 在前景接收使用者互動,任務 A 則在背景等待繼續。

任務是一個內聚單元,當使用者開始新任務或前往主畫面時,可以移至背景。在背景中,工作中的所有活動都會停止,但工作的返回堆疊會保持不變,也就是說,當另一個工作發生時,該工作會失去焦點,如圖 2 所示。接著,工作可以返回前景,讓使用者從上次中斷的地方繼續。

請考慮以下目前工作 A 的工作流程,其堆疊中有三個活動,包括目前活動下的兩個活動:

  1. 使用者按下主畫面按鈕或手勢,然後從應用程式啟動器啟動新的應用程式。

    主畫面顯示時,工作 A 會進入背景。新應用程式啟動時,系統會為該應用程式啟動一項工作 (工作 B),並建立專屬的活動堆疊。

  2. 與該應用程式互動後,使用者再次返回主畫面,並選取原本啟動任務 A 的應用程式。

    現在,工作 A 移至前景,堆疊中的三個活動都完好無損,堆疊頂端的活動也會繼續執行。此時,使用者也可以切換回工作 B,方法是前往主畫面,然後選取啟動該工作的應用程式圖示,或從「最近」畫面選取應用程式的工作。

多個活動例項

圖 3. 單一活動可多次例項化。

由於返回堆疊中的活動一律不會重新排列,因此如果應用程式允許使用者從多個活動啟動特定活動,系統會建立該活動的新例項並推送至堆疊,而不是將該活動的任何先前例項移至頂端。因此,應用程式中的某個活動可能會多次例項化,甚至從不同任務中例項化,如圖 3 所示。

如果使用者使用返回按鈕或手勢返回,系統會按照開啟順序顯示活動例項,每個例項都有各自的 UI 狀態。不過,如果您不希望活動例項化多次,可以修改這項行為。詳情請參閱「管理工作」。

多視窗模式環境

當應用程式在多視窗模式中同時執行時 (Android 7.0 (API 級別 24) 以上版本支援),系統會分別管理每個視窗的工作。每個視窗可以有多項工作。在 Chromebook 上執行的 Android 應用程式也是如此:系統會以個別視窗為單位管理工作或工作群組。

生命週期回顧

活動和任務的預設行為摘要如下:

  • 當活動 A 啟動活動 B 時,活動 A 會停止,但系統會保留其狀態,例如捲動位置和輸入表單的任何文字。如果使用者在活動 B 中輕觸或使用返回手勢,活動 A 就會恢復並還原狀態。

  • 使用者透過主畫面按鈕或手勢離開工作時,系統會停止目前活動,並將工作移至背景。系統會保留任務中每個活動的狀態。如果使用者稍後選取啟動器圖示來繼續執行工作,該工作就會移至前景,並繼續執行堆疊頂端的活動。

  • 如果使用者輕觸或手勢返回,系統會從堆疊中彈出目前的活動並刪除。堆疊中的前一個活動會繼續執行。活動刪除後,系統「不會」保留活動狀態。

    在 Android 12 (API 級別 31) 以上版本中,根啟動器活動的行為會有所不同。

  • 活動可以多次例項化,甚至可以從其他任務例項化。

管理工作

Android 管理工作和返回堆疊的方式,是將所有連續啟動的活動放在同一個工作中,並採用後進先出的堆疊。這對大多數應用程式來說都非常實用,您通常不必擔心活動與任務的關聯,或活動在返回堆疊中的存在方式。

不過,您可能會想中斷正常行為。舉例來說,您可能希望應用程式中的活動在啟動時開始新任務,而不是放在目前任務中。或者,當您啟動活動時,可能會想將現有例項移至前景,而不是在返回堆疊頂端建立新例項。或者,您可能希望在使用者離開任務時,清除返回堆疊中除了根活動以外的所有活動。

您可以使用 <activity> 資訊清單元素中的屬性,以及傳遞至 startActivity() 的意圖中的旗標,執行上述操作及其他操作。

以下是可用於管理工作的主要 <activity> 屬性:

以下是可使用的主要意圖旗標:

以下各節將說明如何使用這些資訊清單屬性和意圖旗標,定義活動與任務的關聯方式,以及活動在返回堆疊中的行為。

以下各節也會說明系統如何在「最近使用的應用程式」畫面中呈現及管理工作和活動。一般來說,您會讓系統定義工作和活動在「最近使用的應用程式」畫面中的呈現方式,不需要修改這項行為。詳情請參閱「『最近使用的應用程式』畫面」。

定義啟動模式

啟動模式可讓您定義活動的新例項如何與目前任務建立關聯。您可以透過下列兩種方式定義啟動模式,詳情請見後續章節:

因此,如果活動 A 啟動活動 B,活動 B 可以在資訊清單中定義與目前任務的關聯方式,而活動 A 則可以使用意圖旗標,要求活動 B 與目前任務的關聯方式。

如果兩個活動都定義了活動 B 與任務的關聯方式,則系統會優先採用活動 A 的要求 (在意圖中定義),而非活動 B 的要求 (在資訊清單中定義)。

使用資訊清單檔案定義啟動模式

在資訊清單檔案中宣告活動時,您可以使用 <activity> 元素的 launchMode 屬性,指定活動與任務的關聯方式。

您可以為 launchMode 屬性指派五種啟動模式:

  1. "standard"
    預設模式。系統會在啟動活動的任務中建立新的活動例項,並將意圖轉送到該例項。活動可多次例項化,每個例項可屬於不同任務,且一項任務可有多個例項。
  2. "singleTop"
    如果目前任務的頂端已有活動例項,系統會呼叫該例項的 onNewIntent() 方法,將意圖轉送至該例項,而不會建立新的活動例項。活動會多次例項化,每個例項可屬於不同任務,一個任務可有多個例項 (但前提是返回堆疊頂端的活動「不是」活動的現有例項)。

    舉例來說,假設某項工作的返回堆疊包含根活動 A,以及位於上方的活動 B、C 和 D (因此堆疊為 A-B-C-D,D 位於頂端)。意圖到達 D 類型的活動。如果 D 具有預設的 "standard" 啟動模式,系統會啟動該類別的新例項,堆疊會變成 A-B-C-D-D。不過,如果 D 的啟動模式是 "singleTop",D 的現有例項會透過 onNewIntent() 接收意圖,因為該例項位於堆疊頂端,且堆疊仍為 A-B-C-D。另一方面,如果意圖是針對 B 類型的活動,則即使啟動模式為 "singleTop",系統也會將 B 的新例項新增至堆疊。

  3. "singleTask"
    系統會在新任務的根層級中建立活動,或在設有同樣相依性的現有任務中尋找該活動。如果活動已有例項,系統會呼叫 onNewIntent() 方法,將意圖轉送至現有例項,而不是建立新例項。同時,系統會刪除該活動之上的所有其他活動。
  4. "singleInstance"
    行為與 "singleTask" 相同,只是系統不會在容納例項的工作中啟動任何其他活動。該活動始終是其任務中唯一的成員。由這個活動啟動的任何活動都會在個別任務中開啟。
  5. "singleInstancePerTask"
    活動只能以任務的根層級活動 (建立該任務的第一個活動) 形式執行,因此這個活動在任務內只會有一個例項。與 singleTask 啟動模式不同,如果設定 FLAG_ACTIVITY_MULTIPLE_TASK 或 FLAG_ACTIVITY_NEW_DOCUMENT 旗標,這項活動可在不同任務中啟動多個例項。

舉例來說,Android 瀏覽器會在 <activity> 中指定 singleTask,因此其活動「不會」放在應用程式的工作中。而是會啟動瀏覽器的新工作,或將現有的瀏覽器背景工作移至前景。

無論活動是在新任務中啟動,還是與啟動活動的任務相同,返回按鈕和手勢一律會將使用者帶往上一個活動。不過,如果您啟動的活動指定 singleTask 啟動模式,且該活動的例項存在於背景任務中,則整個任務都會移至前景。此時,返回堆疊會包含堆疊頂端向前移動的工作中的所有活動。圖 4 顯示這類情境。

圖 4. 啟動模式為 "singleTask" 的活動如何加入返回堆疊。如果活動已是背景任務的一部分,且有自己的返回堆疊,則整個返回堆疊也會移至前景,並置於目前任務之上。

如要進一步瞭解如何在資訊清單檔案中使用啟動模式,請參閱 <activity> 元素說明文件。

使用 Intent 旗標定義啟動模式

啟動活動時,您可以在傳送至 startActivity() 的意圖中加入標記,修改活動與其任務的預設關聯。您可以使用下列標記修改預設行為:

FLAG_ACTIVITY_NEW_TASK

系統會在新工作中啟動活動。如果啟動的活動已有任務正在執行,系統會將該任務移至前景,並還原其最後狀態,活動則會在 onNewIntent() 中接收新的意圖。

這會產生與上一節討論的 "singleTask" launchMode 值相同的行為。

FLAG_ACTIVITY_SINGLE_TOP

如果啟動的活動是目前活動,且位於返回堆疊頂端,則現有例項會收到 onNewIntent() 呼叫,而不會建立新的活動例項。

這會產生與上一節討論的 "singleTop" launchMode 值相同的行為。

FLAG_ACTIVITY_CLEAR_TOP

如果啟動的活動已在目前任務中執行,系統不會啟動該活動的新例項,而是會刪除該活動上方的所有其他活動。意圖會透過 onNewIntent() 傳送至活動的已恢復例項 (目前位於頂端)。

launchMode 屬性沒有任何值會產生這種行為。

FLAG_ACTIVITY_CLEAR_TOP 最常與 FLAG_ACTIVITY_NEW_TASK 搭配使用。這些旗標搭配使用時,會找出其他任務中的現有活動,並將其置於可回應意圖的位置。

處理相似度

相依性表示活動「偏好」屬於哪項任務。根據預設,同一個應用程式的所有活動都具有相同的相依性,也就是「偏好」位於同一個任務中。

不過,您可以修改活動的預設親和性。在不同應用程式中定義的活動可以共用相依性,而在同一個應用程式中定義的活動則可以指派不同的任務相依性。

您可以使用 <activity> 元素的 taskAffinity 屬性修改活動的相依性。

taskAffinity 屬性會採用字串值,該值必須與 <manifest> 元素中宣告的預設套件名稱不同,因為系統會使用該名稱來識別應用程式的預設工作相似性。

親和性會在以下兩種情況發揮作用:

  1. 啟動活動的意圖包含 FLAG_ACTIVITY_NEW_TASK 旗標時。

    根據預設,新活動會在呼叫 startActivity() 的活動工作中啟動。並推送至與呼叫端相同的返回堆疊。

    不過,如果傳遞至 startActivity() 的意圖包含 FLAG_ACTIVITY_NEW_TASK 旗標,系統會尋找其他任務來存放新活動。這通常是新工作。但其實沒那麼困難。如果現有任務與新活動具有相同的相依性,系統就會在該任務中啟動活動。如果沒有,則會開始新任務。

    如果這個旗標導致活動啟動新任務,且使用者透過主畫面按鈕或手勢離開,則必須提供某種方式讓使用者返回該任務。部分實體 (例如通知管理工具) 一律會在外部工作中啟動活動,絕不會在自己的工作中啟動,因此一律會將 FLAG_ACTIVITY_NEW_TASK 放入傳遞至 startActivity() 的意圖中。

    如果外部實體可以使用這個旗標叫用您的活動,請為使用者提供獨立的返回方式,例如在任務的根活動上,透過 CATEGORY_LAUNCHER 篩選器提供啟動器圖示。詳情請參閱「啟動工作」。

  2. 活動的 allowTaskReparenting 屬性設為 "true"。

    在這種情況下,當具有相依性的任務移至前景時,活動可以從啟動活動的任務移至該任務。

    舉例來說,假設旅遊應用程式中定義了一項活動,可回報所選城市的氣候狀況。這項活動與同一應用程式中的其他活動具有相同的相依性 (預設應用程式相依性),且可透過這項屬性重新設定父項。

    當其中一個活動啟動天氣回報活動時,該活動一開始會與您的活動屬於同一個任務。不過,當旅遊應用程式的工作移至前景時,天氣回報活動會重新指派給該工作,並在其中顯示。

清除返回堆疊

如果使用者長時間離開任務,系統會清除任務中除了根活動以外的所有活動。使用者返回任務時,系統只會還原根層級活動。系統之所以會這樣做,是因為假設使用者在經過一段時間後,已放棄先前執行的操作,並返回工作開始新的操作。

您可以使用一些活動屬性來修改這項行為:

alwaysRetainTaskState
如果將任務根活動的這個屬性設為 "true",就不會發生上述預設行為。即使經過很長一段時間,工作仍會保留堆疊中的所有活動。
clearTaskOnLaunch

在任務的根活動中,如果將這項屬性設為 "true",則每當使用者離開任務並返回時,系統都會清除任務,直到根活動為止。換句話說,這與 alwaysRetainTaskState 相反。使用者一律會返回任務的初始狀態,即使只是短暫離開任務也一樣。

finishOnTaskLaunch

這個屬性與 clearTaskOnLaunch 類似,但只會對單一活動執行,而非整個任務。這項屬性也可能導致根活動以外的任何活動結束。如果設為 "true",活動只會在目前的工作階段中保留為任務的一部分。如果使用者離開後返回工作,該工作就不會再顯示。

開始執行工作

您可以為活動提供意圖篩選器,並將 "android.intent.action.MAIN" 指定為動作,"android.intent.category.LAUNCHER" 指定為類別,將活動設為工作的進入點:

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

這個意圖篩選器會在應用程式啟動器中顯示活動的圖示和標籤,讓使用者可以啟動活動,並返回活動建立的工作。

第二項能力很重要。使用者必須能離開工作,然後再透過這個活動啟動器返回工作。因此,只有在活動具有 ACTION_MAIN 和 CATEGORY_LAUNCHER 篩選器時,才使用 "singleTask" 和 "singleInstance" 這兩種啟動模式,將活動標示為一律啟動任務。

舉例來說,假設缺少篩選器,可能會發生以下情況:意圖啟動 "singleTask" 活動,啟動新任務,使用者花一些時間處理該任務。使用者接著使用「主畫面」按鈕或手勢。工作現在會移至背景,不會顯示。現在,使用者無法返回該任務,因為應用程式啟動器中沒有該任務。

如果您不希望使用者返回某個活動,請將 <activity> 元素的 finishOnTaskLaunch 設為 "true"。詳情請參閱「清除返回堆疊」。

如要進一步瞭解「最近使用的應用程式」畫面如何呈現及管理工作和活動,請參閱「『最近使用的應用程式』畫面」。

其他資源