工作與返回堆疊

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

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

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

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

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

系統不會重新排列堆疊中的活動,只會在目前活動啟動及從堆疊中推送至堆疊,並透過返回按鈕或手勢關閉這些活動。因此,返回堆疊會依照「後進先出」物件結構運作。圖 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 的堆疊中有三個活動,包括目前活動下的兩個活動:目前工作 A 的工作流程如下:

  1. 使用者使用主畫面按鈕或手勢,再透過應用程式啟動器啟動新的應用程式。

    當主畫面出現時,工作 A 會在背景中執行。新的應用程式啟動時,系統會使用該應用程式本身的活動堆疊啟動該應用程式的工作 (工作 B)。

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

    現在,工作 A 位於前景,也就是堆疊中的所有三個活動都完整,且堆疊頂端的活動會繼續執行。此時,使用者也可以返回工作 B,方法是前往主畫面並選取啟動這項工作的應用程式圖示,或是在「最近使用」畫面中選取應用程式的工作。

多個活動例項

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

由於返回堆疊中的活動絕對不會重新排列,如果應用程式可讓使用者從多個活動啟動特定活動,系統會為該活動建立新的執行個體並推送至堆疊上,而不是將任何先前的活動例項推送到頂端。因此,即便來自不同的任務,應用程式中的一項活動可能會多次例項化,如圖 3 所示。

如果使用者利用「返回」按鈕或手勢向後瀏覽,活動例項會按照開啟順序顯示,而且每個例項都有自己的 UI 狀態。不過,如果您不希望活動多次執行個體化,則可修改此行為。詳情請參閱管理工作相關章節。

多視窗環境

如果應用程式在多視窗化環境中同時執行 (Android 7.0 (API 級別 24) 以上版本支援),系統會分別管理每個視窗的工作。每個視窗都可以有多項工作。在 Chromebook 上執行的 Android 應用程式也同樣適用:系統管理每個視窗的工作或工作群組。

生命週期重點回顧

以下摘要列出活動和工作的預設行為:

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

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

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

    如果應用程式在搭載 Android 12 以上版本的裝置上執行,根啟動器活動會有不同的行為

  • 即使從其他任務,活動也可以多次例項化。

管理工作

Android 會管理工作和返回堆疊,方法是將相同工作中依序啟動的所有活動安排在最後的退出堆疊中。這種做法適用於大多數應用程式,而且您通常不需要擔心活動與工作建立關聯,也不必擔心活動在返回堆疊中如何存在。

不過,您可能會想中斷正常的行為。例如,您可能希望應用程式中的活動在啟動時開始新任務,而不是放置在目前的任務中。或者,當您啟動活動時,可能會想要轉送現有的執行個體,而不要在返回堆疊上方建立新執行個體。或者,您可能希望在使用者離開任務時,清除所有根活動以外的所有活動。

您可以使用 <activity> 資訊清單元素中的屬性和傳送至 startActivity() 的意圖中的標記來執行此類操作。

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

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

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

此外,我們也要討論工作和活動在「最近使用」畫面中的呈現與管理方式。一般來說,您可讓系統定義工作和活動在「最近使用」畫面中的表示方式,這樣就不需要修改這項行為。詳情請參閱「最近使用畫面」。

定義啟動模式

啟動模式可讓您定義活動的新執行個體與目前任務建立關聯的方式。您可以透過兩種方式定義啟動模式,詳情請參閱以下各節:

因此,如果活動 A 啟動了活動 B,活動 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" 也一樣。

  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"。詳情請參閱「清除返回堆疊」一節。

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

其他資源