Android開發-API指南-任務和回退棧

來源:互聯網
上載者:User

標籤:des   android   style   blog   http   ar   io   color   os   

Task 和 Back Stack

英文原文: http://developer.android.com/guide/components/tasks-and-back-stack.html
採集(更新)日期:2014-12-16

在本文中
  1. 儲存 Activity 狀態
  2. 管理多個 Task
    1. 指定啟動模式
    2. 處理 affinity
    3. 清理回退棧k
    4. 啟動任務
相關文章
  1. Multitasking the Android Way
參閱
  1. Android 設計 : 導航
  2. <activity> manifest 元素
  3. Overview Screen

一個應用程式通常包含多個 Activity。 每個 Activity 都應該圍繞一類 Action 來進行設計,這個 Action 可供使用者執行並能啟動其他 Activity。 比如,一個 email 應用可以用一個 Activity 列出所有新郵件,當使用者選中其中一封時,再開啟一個新的 Activity 來顯示詳情。

Activity 甚至可以啟動當前裝置上另一個應用中的 Activity。 比如,如果當前應用需要發送 email,可以定義一個 Intent 來執行“send” action,此 Intent 中可以包含一些資料,如 email 地址、本文等。 然後,會開啟一個已聲明能夠處理這類 Intent 的屬於其他應用的 Activity。 現在是一個發送 email 的 Intent,所以會開啟一個 email 應用的“建立郵件”(compose) Activity(如果同時有多個 Activity 支援該類 Intent,則系統會讓使用者選用其中一個)。 當 email 發送完畢後,將會恢複之前的 Activity ,看起來 email Activity 就像是當前應用中的一部分一樣。 雖然多個 Activity 可能來自不同的應用,通過把他們放入同一個Task,Android 系統可以提供連貫無縫的使用者體驗。

任務(Task)是多個 Activity 的集合,使用者進行操作時將與這些 Activity 進行互動。 這些 Activity 按照啟動順序依次被存入“回退”棧(“Back Stack”)中。

大部分 Task 都啟動自主畫面(Home)。當使用者點擊應用程式列表(Application Launcher)中的表徵圖(或主畫面上的快捷表徵圖)時,應用程式的 Task 就進入前台。 如果該應用的 Task 不存在(最近沒有使用過該應用),則會建立一個任務,該應用的主(“main”)Activity 將作為回退棧的根 Activity 被開啟。

如果當前 Activity 啟動了另一個 Activity,則新的 Activity 將被壓入棧頂並獲得焦點。 前一個 Activity 仍然儲存在棧中,但是處於停止狀態(Stop)。在 Activity 處於停止狀態時,系統會儲存使用者介面的目前狀態。 當使用者按下回退鍵時,當前 Activity 將從棧頂彈出(被銷毀),前一個 Activity 將被恢複(resume)(之前的使用者介面狀態同時被還原)。 Activity 在回退棧中的順序永遠不會改變,只能壓入(push)和彈出(pop)——被當前 Activity 啟動時壓入棧頂,使用者用回退鍵離開時彈出。 這樣,回退棧 以“後進先出”的方式運行。圖1 以時間軸的形式展示了多個 Activity 切換時各時間點的回退棧狀態。


圖 1. , Task 中的每個新 Activity 都會相應在 Back Stack 中增加一項。當使用者按下回退鍵時,當前 Activity 將被銷毀,前一個 Activity 將被恢複。

如果使用者連續不停地按下回退鍵,則回退棧中所有 Activity 都會被依次彈出並顯示出來,直至使用者回到主畫面(或是啟動本任務的 Activity)。 當所有 Activity 都從棧中彈出後,本任務就此消失。


圖 2. 兩個任務 :Task B 在前台與使用者互動,而 Task A 在後台等待喚醒。


圖 3. Activity A 被多次執行個體化。

任務(Task)是一種整體單位, 當使用者啟動一個新任務或回到主畫面時,當前任務就可以轉到“後台”。 當任務處於後台時,其中所有的 Activity 都處於停止狀態,但是這個任務的回退棧仍然完整保留——2所示,在其他任務獲得焦點期間,該任務只是失去焦點而已。 任務可以再次返回“前台”,所以使用者能夠重新回到剛才離開的狀態繼續工作。 比如,假定當前任務(A任務)的回退棧中共有3個 Activity —— 下面還有兩個 Activity。 這時,使用者按下Home鍵,並從應用程式列表中啟動了一個新的應用。 當主畫面出現時,A任務進入後台。 當新的應用啟動時,系統會為它再開一個任務 (B任務),新應用中的 Activity 將存放其中。 新應用使用完畢後,使用者再次返回主畫面,並選擇執行那個啟動A任務的應用。 現在,A任務回到前台——棧中的三個 Activity 仍然完好,位於棧頂的 Activity 恢複運行。 這時,使用者仍然可以切回B任務,通過返回主畫面並選擇執行相應的應用表徵圖即可,或者在最近工作清單(overview screen中選擇相應任務)。 以上便是 Android 多任務的執行個體。

注意: 後台可以同時保留多個任務。但是,假如使用者還需要運行很多背景工作,則系統可能會銷毀後台 Activity 以騰出記憶體,這會導致 Activity 狀態的丟失。 請參閱後續章節 Activity 狀態。

因為回退棧中的 Activity 順序永遠不會改變,如果某個 Activity 允許被使用者啟動多個執行個體,則新建立的執行個體會壓入棧頂(而不是開啟已位於棧頂的 Activity 執行個體)。 於是,一個 Activity 可能會被初始化多次(甚至會位於不同的任務中),3所示。 這時,如果使用者用回退鍵回退,則 Activity 的每個執行個體都會沿著啟動順序顯示回去(包括使用者介面的原有狀態)。 當然,如果不想讓 Activity 被多次執行個體化,可以修改這種方式。修改的方法將在後續章節 管理多個任務 中討論。

下面把 Activity 和任務的預設特性匯總一下:

  • 當 Activity A 啟動 Activity B 時,Activity A 將被停止,但系統仍會儲存其狀態(如捲軸位置和表單中錄入的文字)。 如果使用者在 Activity B 中按下回退鍵,則 Activity A 恢複運行,狀態也將被恢複。
  • 當使用者按下Home鍵離開任務時,當前 Activity 被停止,當前任務轉入後台。 系統會儲存任務中每個 Activity 的狀態。如果使用者將來通過啟動應用表徵圖來恢複任務,那麼該任務就會回到前台,棧頂的 Activity 會恢複運行。
  • 如果使用者按下回退鍵,當前 Activity 將從回退棧中彈出並被銷毀。 棧中的前一個 Activity 將恢複運行。 當 Activity 被銷毀後,系統不會保留其狀態。
  • Activity 可以被多次執行個體化,甚至可以位於不同的任務中。

導航設計

關於 Android 各應用程式間跳轉的工作機制,請參閱 Android 導航 設計指南。

儲存 Activity 狀態

如上所述,系統預設會在 Activity 停止時儲存其狀態。這樣,當使用者返回時,使用者的介面就能保持與離開時的一樣。 不過,你可以——也應該——用回調方法來主動地儲存 Activity 的狀態,以便應對 Activity 被銷毀並重建的情況。

當停止 Activity 時(比如啟動了一個新 Activity 或者任務轉入後台),出於騰出記憶體的需要,系統也許會完全銷毀該 Activity。 這樣該 Activity 的狀態資訊將會丟失。如果發生這種情況,系統仍然是記得該 Activity 已經被置入回退棧了。 但是當它位於棧頂時,系統必須重建它(而不是恢複)。為了防止使用者工作內容的丟失,你應該主動儲存這些內容,通過實現 Activity 的 onSaveInstanceState() 回調方法即可。

關於如何儲存 Activity 狀態的詳情,請參閱文檔 Activities。

多任務管理

如上所述—— Android 把所有已啟動的 Activity 相繼放入同一個任務中以及一個“後入先出”棧。 這種管理工作和回退棧的方式適用於大多數應用, 不必去操心各 Activity 如何與任務關聯,以及如何彈出回退棧。 不過,有時也許有必要改變這種通常的運行方式。 也許某個 Activity 需要啟動一個新的任務 (而不是被放入當前任務); 或者,啟動 Activity 時需要調出已有的某個執行個體(而不是在回退棧頂建立一個新的執行個體); 或者,當使用者離開任務時只需要保留根 Activity,而回退棧中的其他 Activity 都要被清掉。

不僅如此,利用 <activity> manifest 元素的屬性和傳入 startActivity() 的 Intent 中的標誌位屬性,還可實現更多需求。

這裡可以使用的 <activity> 屬性主要包括:

  • taskAffinity
  • launchMode
  • allowTaskReparenting
  • clearTaskOnLaunch
  • alwaysRetainTaskState
  • finishOnTaskLaunch

可用的 intent 標誌主要有:

  • FLAG_ACTIVITY_NEW_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_SINGLE_TOP

下一節介紹了如何利用這些 manifest 屬性和 Intent 標誌位屬性來定義 Activity 與任務的關聯性,以及他們在回退棧中的工作方式。

並且,後續還單獨討論了任務和 Activity 如何在最近工作清單中分別表示和管理的。 更多資訊請參閱 Overview Screen。 通常,應該讓系統來決定任務和 Activity 在最近工作清單中的表示方式,不需要去改動它。

警告: 大多數應用不應該改變 Activity 和任務的預設工作方式。 如果確定有必要修改預設,請保持謹慎,確保在啟動 Activity 和從其他 Activity 用 回退鍵返回時的可用性。 對於那些可能與使用者預期效果衝突的導航方式,都要確保進行測試。定義啟動模式

啟動模式定義了 Activity 新執行個體與當前任務的關聯方式。定義啟動模式的方法有兩種:

  • 使用 manifest 檔案

    在 manifest 檔案中聲明一個 Activity 時,可以指定啟動它時與任務的關聯方式。

  • 使用 Intent 中的標誌位屬性

    調用 startActivity() 時,可以在 Intent 中包含一個標誌,用於指明新 Activity 如何(是否)與當前任務相關聯。

這樣,如果 Activity A 啟動了 Activity B,則 Activity B 可以在 manifest 中定義它如何與當前任務關聯(如果存在的話), 並且,Activity A 也可以指定 Activity B 與當前任務的關聯關係。 如果兩者都定義了,則 Activity A 的請求(Intent 中定義)優先於 Activity B 的定義(在 manifest 檔案中)。

注意: manifest 檔案中的某些啟動模式在 Intent 標誌中是停用。 反之亦然,Intent 中的某些模式也無法在 manifest 中定義。使用 manifest 檔案

在 manifest 檔案中聲明 Activity 時,你可以利用 <activity> 元素的 launchMode 屬性來設定 Activity 與任務的關係。

launchMode 屬性指明了 Activity 啟動任務的方式。 launchMode 屬性可設為四種啟動模式:

"standard" (預設模式)
預設值。系統在啟動 Activity 的任務中建立一個新的 Activity 執行個體,並把 Intent 傳遞路徑指向它。 該 Activity 可以被執行個體化多次,各個執行個體可以屬於不同的任務,同一個任務中也可以存在多個執行個體。
"singleTop"
如果 Activity 已經存在一個執行個體並位於當前任務的棧頂,則系統會調用已有執行個體的 onNewIntent() 方法把 Intent 傳遞給已有執行個體,而不是建立一個新的 Activity 執行個體。 Activity 可以被執行個體化多次,各個執行個體可以屬於不同的任務,同一個任務中可以存在多個執行個體(但僅當回退棧頂的 Activity 執行個體不是該 Activity 的)。

比如,假定某個任務的回退棧中包含了根 Activity A 和 Activity B、C、D(順序是 A-B-C-D;D 在棧頂)。 這時,傳來了一個啟動 Activity D 的 Intent。 如果 Activity D 的啟動模式是預設的"standard",則會啟動 Activity D 類的一個新執行個體,棧的內容變為 A-B-C-D-D。 但是,如果 Activity D 的啟動模式是"singleTop",則已有的 Activity D 執行個體會通過 onNewIntent() 接收這個 Intent,因為該執行個體位於棧頂——棧中內容仍然維持 A-B-C-D 不變。 當然,如果此 Intent 是要啟動 Activity B 的,則 Activity B 的一個新執行個體還是會被加入棧中,即使 Activity B 的啟動模式是"singleTop"也是如此。

注意: 一個 Activity 的新執行個體建立完畢後,使用者可以按 回退鍵返回前一個 Activity。 但是如果是由 Activity 的已有執行個體來處理剛到達的 Intent ,那使用者就無法用 回退鍵回到 onNewIntent() 中 Intent 到來之前的 Activity 狀態了。
"singleTask"
系統將建立一個新的任務,並把 Activity 執行個體作為根成員放入其中。 但是,如果該 Activity 已經在其他任務中存在執行個體了,則系統會調用已有執行個體的 onNewIntent() 方法把 Intent 傳給已有執行個體,而不是再建立一個新執行個體。 本類 Activity 同一時刻只能存在一個執行個體。 注意: 雖然 Activity 啟動了一個新的任務,但使用者仍然可以用 回退鍵返回前一個 Activity。
"singleInstance"
除了系統不會把其他 Activity 放入當前執行個體所在的任務之外,其他均與"singleTask"相同。 Activity 總是它所在任務的唯一成員;它所啟動的任何 Activity 都會放入其他任務中。

再舉個例子,Android 的瀏覽器應用就把 網頁瀏覽器 Activity 聲明為總是在它自己獨立的任務中開啟——把 <activity> 設為singleTask模式。 這意味著,如果某個應用提交 Intent 來開啟 Android 的瀏覽器,則其 Activity 不會被放入該應用所在的任務中。 而是或為瀏覽器啟動一個新的任務;或者瀏覽器已經在後台運行,只要把任務調入前台處理新的 Intent 即可。

無論 Activity 是在一個新的任務中啟動,還是位於其他已有的任務中,使用者總是可以用回退鍵返回到前一個 Activity 中。 但是,如果啟動了一個啟動模式設為singleTask的 Activity,且有一個背景工作中已存在其執行個體的話,則這個背景工作就會整體切換到前台。 這時,當前的回退棧中就包含了這個轉入前台的任務中的全部 Activity,且位置是在棧頂。 圖 4 就展示了這種情境。


圖 4. 啟動模式為“singleTask”的 Activity 如何加入回退棧的示意。 如果 Activity 已經是在背景工作中並帶有自己的回退棧,則整個後台回退棧都會被帶到前台,並放入當前任務的棧頂。

關於在 manifest 檔案中使用啟動模式的詳情,請參閱 <activity> 元素文檔,其中詳細描述了launchMode屬性及其可用值。

注意: 利用 launchMode 屬性為 Activity 設定的啟動模式可以被啟動 Activity 的 Intent 標誌位屬性所覆蓋,這將在下一節中描述。使用 Intent 標誌位屬性

在啟動 Activity 時,可以在傳給 startActivity() 的 Intent 中包含相應標誌位,以便修改 Activity 與任務的預設關係。 可用於修改預設啟動模式的標誌位包括:

FLAG_ACTIVITY_NEW_TASK在新的任務中啟動 Activity。 如果要啟動的 Activity 已經在某任務中運行,則那個任務將被調入前台,最後儲存的狀態也將恢複,Activity 將在 onNewIntent() 方法中接收到這個新 Intent。

這個過程與上一節的"singleTask" 啟動模式相同。

FLAG_ACTIVITY_SINGLE_TOP如果要啟動的 Activity 就是當前 Activity(位於回退棧 頂),則已存在的執行個體將接收到一個 onNewIntent() 調用,而不是建立一個 Activity 的新執行個體。

這個過程與上一節的 "singleTop" 啟動模式相同。

FLAG_ACTIVITY_CLEAR_TOP如果要啟動的 Activity 已經在當前任務中運行,則不再啟動一個新的執行個體,且所有在其上面的 Activity 將被銷毀, 然後通過 onNewIntent() 傳入 Intent 並恢複 Activity(不在棧頂)的運行,

此種模式在launchMode 屬性中沒有對應值。

FLAG_ACTIVITY_CLEAR_TOP 常與 FLAG_ACTIVITY_NEW_TASK 一起使用。 這表示先找到其他任務中已存在的 Activity,再把此 Activity 放入可以響應 Intent 的位置。

注意: 如果 Activity 的啟動模式配置為"standard",則它會先被移除出棧,再建立一個新的執行個體來處理這個 Intent。 這是因為啟動模式為 "standard" 時,總是會建立一個新的執行個體。處理 affinity

affinity 表示 Activity 期望被放入的任務。 預設情況下,同 表示 Activity 期望被放入的任務。 預設情況下,同一個應用中的所有 Activity 都擁有同一個 affinity 值。 因此,同一個應用中的所有 Activity 預設都期望位於同一個任務中。 不過,你可以修改 Activity 預設的 affinity 值。 不同應用中的 Activity 可以共用同一個 affinity 值,同一個應用中的 Activity 也可以賦予不同的任務 affinity 值。

你可以用<activity> 元素的 taskAffinity 屬性修改 activity 的 affinity,

taskAffinity 屬性是一個字串值,必須唯一,並與 <manifest> 元素定義的預設包名稱有所區別,因為系統用這個包名稱作為應用的預設任務的 affinity 值。

affinity 將在以下兩種情況下發揮作用:

  • 啟動 Activity 的 Intent 中包含了 FLAG_ACTIVITY_NEW_TASK。

    預設情況下,一個新的 Activity 將被啟動並放入調用 startActivity() 的 Activity 所在的任務中,且壓入調用者所處的回退棧頂部。 不過,如果傳給 startActivity() 的 Intent 包含了 FLAG_ACTIVITY_NEW_TASK 標誌,則系統會尋找另一個任務並將新 Activity 放入其中。 這時常常會新開一個任務,但並非一定如此。 如果一個已有任務的 affinity 值與新 Activity 的相同,則 Activity 會放入該任務中。 如果沒有,則會建立一個任務。

    如果本標誌使得 Activity 啟動了一個新的任務,則當使用者按下 Home 鍵離開時,必須採取一些措施以便使用者能回到此任務。 某些應用(比如通知管理器)總是讓 Activity 放入其它任務中啟動,而不是放入自己的任務中。 因此,它們總是把 FLAG_ACTIVITY_NEW_TASK> 標誌放入傳給startActivity() 的 Intent 中。 如果你的 Activity 可以被外部應用用此標誌來啟動,請注意使用者會用相對獨立的方式返回啟動時的任務, 比如通過啟動表徵圖(這類任務的根 Activity 帶有一個 CATEGORY_LAUNCHER Intent 過濾器;參閱下節 啟動任務)。

  • 當一個 Activity 的 allowTaskReparentingl設定為"true"。

    這種情況下,當某個任務進入前台時,而此 Activity 的 affinity 值又與其相同,則該 Activity 可以從啟動時的任務移入這個任務中。

    比如,假定某旅遊應用中含有一個根據所選城市預報天氣的 Activity。 它的 affinity 與同一應用中的其他 Activity 一樣(整個應用預設的 affinity),且它允許通過本屬性重新指定所屬任務。 當其他應用的 Activity 啟動此天氣預報 Activity 時,它將位於此應用的任務中。 然而,當旅遊應用的任務進入前台時,天氣預報 Activity 將會重新回到其任務中並在其中顯示。

提示:如果一個.apk 檔案中包含了多個“應用”,就可能需要用 taskAffinity 屬性來指定每個“應用”中 Activity 的 affinity 值。清理回退棧

如果使用者離開某個任務已有很長時間了,系統將會僅保留一個根 Activity,而把其他 Activity 都清除掉。 當使用者返回該任務時,只有根 Activity 會被恢複。 系統之所以這麼處理,是因為經過了很長時間後,使用者應該是要放棄之前進行的工作,而返回任務是為了開始新的工作。

你可以利用 Activity 的某些屬性來改變這種處理方式:

alwaysRetainTaskState
如果任務中的根 Activity 的此屬性設為 "true" ,則預設的清理方式不會進行。 即使過了很長的時間,任務中所有的 Activity 也都會被保留在棧中。
clearTaskOnLaunch
如果任務中根 Activity 的此屬性設為 "true",則只要使用者離開並再次返回該 task,棧就會被清理至根 activity。 也就是說,正好與 alwaysRetainTaskState 相反。使用者每次返回任務時看到的都是初始狀態,即使只是離開一會兒。
finishOnTaskLaunch
此屬性類似於 clearTaskOnLaunch ,只是它只對某一個 Activity 生效,而不是整個任務。 這可以使得任何一個 Activity 消失,包括 根 Activity。 如果 Activity 的此屬性設為 "true",則只會保留 task 中當前 session 所涉及的內容。 如果使用者離開後再返回任務,它就不存在了。
啟動任務

你可以把某個 Activity 設為任務的入口,通過發送一個 Action 為 "android.intent.action.MAIN"、category 為 "android.intent.category.LAUNCHER" 的 Intent 即可。 比如:

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

這種 Intent 過濾器將會把此 Activity 的表徵圖和文字標籤(label)作為應用程式的啟動表徵圖來顯示, 使用者可以啟動此 Activity,並且在之後任何時候返回建立時的任務。

第二種特性非常重要:使用者必須能夠離開一個任務,之後還能再回來使用這個啟動任務的 Activity。 因此,標明 Activity 每次都會啟動任務的這兩種 啟動模式: "singleTask" 和 ""singleInstance" 應該僅對帶有 ACTION_MAIN 和 CATEGORY_LAUNCHER 過濾器的 Activity 才能使用。 想象一下,如果未給出這類過濾器會發生什麼: 某個 Intent 啟動了一個 "singleTask" 模式的 Activity, 並建立了一個任務,使用者在此任務中工作了一段時間。 然後他按下了 Home 鍵。 當前任務就轉入後台,變為不可見狀態。這時使用者就無法再回到該任務了,因為在應用程式列表(Application Launcher)中沒有相應的項目顯示。

對於那些不想讓使用者返回的 Activity,把 <activity> 元素的 finishOnTaskLaunch 設為 "true" 即可(參閱 清理回退棧。

關於在最近應用列表中如何顯示並管理工作和 Activity 的更多資訊,請參閱 Overview Screen。

Android開發-API指南-任務和回退棧

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.