Android基礎之Activity 執行模式與回退棧
基本概念
LaunchMode 定義的是activity例項與task之間的關係,可以通過下面的兩種方式來定義:
-
使用manifest檔案·
利用清單檔案中的<activity>標籤下的launchmode屬性。
-
使用intent flags
呼叫startActivity(),在intent中指定flag。
在Activity A中啟動B,可以利用Activity B在清單中的launchmode定義,也可以在A中呼叫startActivity()的時候通過intent的flag傳入,當兩種方式都有定義,intent的flag引數會覆蓋掉B原有的定義。
使用manifest檔案
利用Activity 元素的launchMode屬性
launchMode屬性指定Activity如何被執行到一個task中。launchMode的值有四種:
"standard" (the default mode)
預設, 每次啟動Activity系統都會產生一個新的例項,並且把intent傳送給新產生的例項,這個Activity可以被例項化多次,每個例項可以屬於不同的task,每個task也可以保有多個此Activity的例項。
"singleTop"
如果當前task 的回退棧棧頂已經存在一個此Activity的例項,系統通過呼叫這個例項的onNewIntent()方法把intent傳送給這個Activity例項,而不是建立一個新的此Activity的例項。這個Activity也可以被例項化多次,每個例項可以屬於不同的task,每個task可一個保有多個例項(僅限於此Activity已存在的例項不在棧頂)
注意:
應用場合如下:不想出現2個同樣的activity在頂部。比如使用者正在一個activity閱讀資訊,這時來了notification,使用者點選後應該更新這些資訊,而不是新建一個activity,這樣在點選back時,就不會出現回到舊資訊activity的情況了。這種情況正是下面這段英語提到的。
Note: When a new instance of an activity is created, the user can press the Back button to return to the previous activity. But when an existing instance of an activity handles a new intent, the user cannot press the Back button to return to the state of the activity before the new intent arrived in onNewIntent()
.
例如,當前回退棧中有A,B,C,D四個Activity,全部是Standard,在D中呼叫startActivity()去啟動B,intent的flag設定成FLAG_ACTIVITY_CLEAR_TOP 和FLAG_ACTIVITY_NEW_TASK,系統發現棧中有B,會先銷燬這個B,再原位置重建B,清空CD,而不是把這個新建的B的例項壓入棧頂,這裡之所以會銷燬B再新建B,因為B的launchmode是Standard,無論什麼情況下啟動,都需要new一個B的例項,但如果此時B是SingleTop的,系統會把這個intent通過onNewIntent傳給已經在棧中的B的例項,不需要銷燬再建立,仍需要清空CD。
"singleTask"
系統會建立一個新的task並且把這個例項放在棧底(此處有疑問,測試發現並不一定是棧底),但是,如果在一個單獨的task中已經存在一個此Activity的例項,系統會把intent通過onNewIntent()傳送給這個例項(測試發現如果在回退棧中,該Activity的上面還有其他Activity,啟動此Activity會清空棧中此Activity上面的其他Activity),而不是建立一個新的例項。同一時間在只有一個此Activity的例項存在於系統中。
"singleInstance"
與SingleTask一樣,不同的是SingleTask的Activity所在的task中可以有其他的Activity,而SingleInstance的Activity獨佔一個task,並且在整個系統中只有唯一的一個例項。由這個Activity啟動的其他Activity都會在新的task中開啟。
另一個例子,系統自帶瀏覽器APP把瀏覽器Activity宣告為SingleTask,通過在Activity標籤裡的launchMode進行指定,這意味著如果你傳送一個intent啟動瀏覽器,不管是為瀏覽器新開啟一個task還是從瀏覽器已經在後臺保有的task中啟動瀏覽器,瀏覽器Activity與你的APP不在同一個task。
不管一個Activity是不是在一個新的task中啟動,點選返回都會返回前一個Activity。不過,如果啟動一個LaunchMode為singleTask的Activity,如果該Activity此時在一個處於後臺的task中,整個task會變成前臺task,此時,回退棧會包含由這個後臺task攜帶過來所有Activity,放在回退棧的棧頂,下圖說明這種情況。
使用intent flags
FLAG_ACTIVITY_NEW_TASK
在一個新的task裡啟動Activity. 如果已經有Activity例項執行在某一task中,啟動這個Activity會把該例項所在的task帶到前臺,由該例項的onNewIntent()來接收新的intent。
FLAG_ACTIVITY_SINGLE_TOP
如果被啟動的Activity就是當前的Activity,這個已經存在的例項通過onNewIntent()接收intent,不會產生新的例項。
FLAG_ACTIVITY_CLEAR_TOP
被啟動的Activity如果已經存執行於當前task,回退棧中所有在此Activity上面的Activity都將被銷燬,此Activity通過onNewIntent()接收新的intent。
例如,一個task中有A,B,C,D,四個Activity,如果D 呼叫startActivtiy()啟動Activity B,C和D會被銷燬,B接收這個intent,回退棧中有A,B。
上例中的Activity B的例項,或者通過onNewIntent()接收新的intent,或者銷燬新建來處理新的intent。如果B的launchmode是standard,並且沒有設定FLAG_ACTIVITY_SINGLE_TOP,那麼B會被銷燬重啟,如果是其他launchmode或者設定了FLAG_ACTIVITY_SINGLE_TOP,則會通過onNewIntent()接收。
FLAG_ACTIVITY_CLEAR_TOP 和FLAG_ACTIVITY_NEW_TASK結合使用會有個不錯的效果。
如果啟動的Activity位於task的底部,它會把所在task帶到前臺,並且清理狀態至root狀態,當從通知欄裡開啟一個Activity的會非常有用。
處理Affinity(此後的文字翻譯自google文件)
Affinity指的是一個Activity偏向於從屬於哪個task,預設情況下,一個APP內的所有Activity互相之間共享一個affinity的值,所以,所有同一APP下的所有Activity都偏向於從屬於同一個task。但是,這個值是可以更改的,不同APP內的Activity可以共享一個affinity,同一個APP內的Activity也可以被分配不同的affinity的值。
affinity的值可以通過修改Activity標籤的taskAffinity屬性來修改。
這個屬性接收一個String的值,必須在manifest標籤範圍內是唯一的值,因為系統是通過名稱來標識APP的affinity的值的。
Affinity作用於以下兩種情況:
- 啟動Activity的intent中包含 FLAG_ACTIVITY_NEW_TASK 的flag
通過startActivity()啟動一個新的Activity時,預設情況下,新的Activity會被壓入與啟動者相同的回退棧中。但是,如果在啟動Activity的時候,使用了FLAG_ACTIVITY_NEW_TASK 這個標誌,系統會為新的Activity尋找一個新的task。通常情況下,是一個新的task。但是也並不是必須的。如果系統中有一個task的affinity值與新的Activity的值相同,新的Activity會被分配到這個task中。如果沒有這樣的task,就啟動一個新的task。如果這個標誌產生了一個新的task,當使用者點選home鍵離開的時候,必須要有某種方式能夠使使用者返回到這個task來。有些實體(例如通知管理器)總是從一個外部task中啟動Activity,所以在通過startActivity()啟動新的Activity時總是需要傳遞FLAG_ACTIVITY_NEW_TASK 這個標誌。如果你有一個Activity可以被外部實體可能這個標誌啟動,注意使用者可以有一種獨立的方式回到啟動它的task,例如點選啟動圖示。
- 當Activity的allowTaskReparenting的值為true
這種情況下,一個Activity可以動啟動它的那個task移動到它的affinity值對應的task中,當那個task回到前臺。例如,假設,一個報告指定城市天氣情況的Activity作為一個旅行APP的一部分,它跟其他處在同一APP的Activity一樣有一個相同的affinity值,並且允許通過這個屬性來調整目標task。當你的一個Activity啟動了這個天氣預報Activity,它預設跟你的Activity在一個task裡,但是,當旅行APP進入到前臺,這個天氣預報Activity又會被重新分配給旅行APP並且在旅行APP內展示。
提示:
如果一個APK檔案從使用者的角度看是多款APP,可能需要這個屬性來設定不同的affinity來關聯不同的APP。
清理回退棧
如果使用者離開一個task太長時間,系統會清除task中的所有Activity僅僅保留這下根Activity。當使用者返回到這個task的時候,只有這個根Activity會被恢復。系統通過這種方式來處理,是因為經過相當長的一段時間之後,使用者已經拋棄他們曾經正在做的事情,計劃再回來的時候做點新的事情。
你可以通過如下屬性來更改這種行為:
- alwaysRetainTaskState
根Activity此屬性設定為true的時候,會保留回退棧中所有Activity的狀態。 - clearTaskOnLaunch
根Activity此屬性設定為true的時候,不管離開應用多久,回退棧中的Activity都會被清理,與alwaysRetainTaskState的情況相反。 - finishOnTaskLaunch
類似於clearTaskOnLaunch,但是它作用於單個Activity,而不是針對整個Task。它可以清理任意一個Activity,包含根Activity。設定為true的時候,只有在當前的會話中會保留此Activity,只要使用者離開,再次回來的時候,此Activity就會被清理。
參考文獻:
Tasks and Back Stack
<activity>
Android 閱讀Tasks and Back Stack文章後的重點摘抄
相關文章
- Android開發之Fragment回退棧AndroidFragment
- Android基礎之Activity全解析Android
- 完全理解android Activity啟動模式LauchMode (深入Activity與任務棧)Android模式
- Android知識點回顧之Activity基礎Android
- Android基礎-Activity基本使用Android
- Java 多執行緒設計模式之基礎概念Java執行緒設計模式
- Android應用鎖之獲取棧頂ActivityAndroid
- Android的Activity啟動與子執行緒更新UIAndroid執行緒UI
- Android之Activity基類封裝Android封裝
- Android 基礎知識——執行緒Android執行緒
- Java基礎之執行緒安全Java執行緒
- JavaSE基礎系列之執行緒Java執行緒
- JavaScript執行環境與執行棧JavaScript
- Android進階;Activity的棧與跳轉(筆記)Android筆記
- Java基礎之執行緒那些事Java執行緒
- java基礎:執行緒方法之joinJava執行緒
- JUC之執行緒池基礎與簡單原始碼分析執行緒原始碼
- 併發與多執行緒基礎執行緒
- Android之ActivityAndroid
- 【多執行緒與高併發】- 執行緒基礎與狀態執行緒
- 一. 執行緒管理之Thread基礎執行緒thread
- 多執行緒之 NSOperation 基礎用法執行緒
- java基礎:執行緒方法之yield方法Java執行緒
- 棧與佇列理論基礎佇列
- 執行緒基礎執行緒
- Android 基礎 -- Activity 生命週期實踐總結Android
- Python基礎之棧與佇列及遞迴目錄Python佇列遞迴
- Java 多執行緒基礎(六)執行緒等待與喚醒Java執行緒
- Android多執行緒基礎 解析Handler機制Android執行緒
- Java基礎之代理模式Java模式
- java基礎之執行緒 認識volatileJava執行緒
- java基礎之執行緒 認識原子類Java執行緒
- 多執行緒基礎之synchronized和volatile執行緒synchronized
- java基礎:執行緒方法之interrupt和sleepJava執行緒
- Android 基礎之 HandlerAndroid
- Activity 知識梳理(2) Activity 棧
- 多執行緒基礎-基礎實現執行緒
- JavaScript深入之執行上下文棧JavaScript