工作與返回堆疊

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

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

任務的生命週期及其返回堆疊

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

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

堆疊中的活動一律不會重新排列,只會在堆疊開始時推送到堆疊中,因為這些活動是由目前活動啟動,並由使用者透過返回按鈕或手勢關閉。因此,返回堆疊是以「後進先出」物件結構的方式運作。圖 1 為時間軸,顯示活動從返回堆疊推送並從返回堆疊中彈出。

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

使用者持續輕觸或做出手勢返回動作時,系統會關閉堆疊中的每個活動,顯示上一個活動,直到使用者返回主畫面,或移至工作開始時執行的任何活動。將所有活動從堆疊中移除後,工作就不再存在。

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

根啟動器活動是使用 ACTION_MAINCATEGORY_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 以上版本的裝置上執行時,這個根啟動器活動的行為不同

  • 活動可以多次例項化,即使是來自其他任務也沒問題。

管理工作

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 類型的活動,則即使 B 的啟動模式為 "singleTop",也會將新的 B 例項新增至堆疊。

  3. "singleTask"
    系統會在新工作的根層級中建立活動,或具有相同相依性的現有任務中尋找該活動。如果活動例項已存在,系統會透過呼叫該執行個體的 onNewIntent() 方法將意圖轉送到現有執行個體,而不會建立新執行個體。而頂端的所有其他活動都會遭到刪除。
  4. "singleInstance".
    行為與 "singleTask" 相同,只是系統不會在保留例項的任務中啟動任何其他活動。該活動始終是其任務中唯一的成員。由此項目啟動的所有活動會在另一個工作中開啟。
  5. "singleInstancePerTask".
    活動只能以任務的根活動 (建立任務的第一個活動) 的形式執行,因此這個活動在任務內只能有一個例項。與 singleTask 啟動模式不同,如果已設定 FLAG_ACTIVITY_MULTIPLE_TASKFLAG_ACTIVITY_NEW_DOCUMENT 旗標,則可在不同任務的多個執行個體中啟動這項活動。

再舉一個例子,Android 瀏覽器應用程式會宣告網路瀏覽器活動一律會以自己的工作開啟,方法是在 <activity> 元素中指定 singleTask 啟動模式。這表示如果應用程式發出意圖以開啟 Android 瀏覽器,其活動「不會」置於與應用程式相同的工作中。相反地,系統會為瀏覽器啟動新工作,如果瀏覽器已在背景執行任務,該工作就會轉而處理新的意圖。

無論活動是在新任務中開始,還是在啟動該活動的活動中啟動,「返回」按鈕和手勢一律會引導使用者前往先前的活動。但是,如果您啟動指定 singleTask 啟動模式的活動,且該活動的執行個體存在於背景工作中,則系統會將整個工作移至前景。此時,返回堆疊會包含堆疊頂端向前的工作的所有活動。圖 4 顯示這種情境。

圖 4. 表示如何在返回堆疊中新增含有啟動模式 "singleTask" 的活動。如果活動已是背景任務的一部分,且具有其返回堆疊,則整個返回堆疊也會在目前任務之上持續向前。

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

使用意圖旗標定義啟動模式

啟動活動時,您可以在傳送至 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_MAINCATEGORY_LAUNCHER 篩選器時,使用將活動標示為「一律啟動任務」的 "singleTask""singleInstance" 這兩種啟動模式。

舉例而言,如果缺少篩選器可能會發生什麼情況:意圖啟動 "singleTask" 活動、啟動新工作,以及使用者花一些時間執行該項工作。接著使用主畫面按鈕或手勢。工作現在會傳送至背景,且未顯示。使用者現在無法返回任務,因為工作不會顯示在應用程式啟動器中。

如果不想讓使用者返回活動,請將 <activity> 元素的 finishOnTaskLaunch 設為 "true"。詳情請參閱「清除返回堆疊」一節。

如要進一步瞭解如何在「最近使用」畫面中表示及管理工作和活動,請參閱「最近使用」畫面

其他資源