launchMode,通俗點說,就是定義了Activity應該如何被launch的。那麼這幾種模式的區別以及應用場景,會有何不同呢?谷歌是基於什麼原因設計這幾種模式的呢?這幾種模式背後的工作原理是什麼呢? ...
launchMode,通俗點說,就是定義了Activity應該如何被launch的。那麼這幾種模式的區別以及應用場景,會有何不同呢?谷歌是基於什麼原因設計這幾種模式的呢?這幾種模式背後的工作原理是什麼呢?
1. 任務和返回棧
在講解launchMode之前,先說說任務(Task)和返回棧(Back Stack,有些譯作回退棧、任務棧)這兩個概念。
A task is a collection of activities that users interact with when performing a certain job. The activities are arranged in a stack—the back stack—in the order in which each activity is opened.
任務是指當完成一個特定的工作時與用戶交互的一系列Activity。這些Activity按照打開的順序存放在一個棧中,即返回棧。
通過定義可以知道,Activity會被按照打開的順序存放。不難猜想,這種存放方式,是為了方便回退操作,也就不難解釋為什麼要用棧去存放。
當用戶點擊啟動app的時候,這個app的返回棧就會跑到前臺,如果這個返回棧不存在的話,就會創建一個。當前Activity啟動另一個Activity的時候,新的Activity就會入棧,在棧頂。如果用戶點擊返回按鈕,當前的Activity就會出棧並銷毀,之前的Activity就會被resume,如果棧為空,就會被銷毀掉。棧中的Activity永遠都不會被重新排序。
返回棧根據是否在前臺,可以分為在前臺顯示的返回棧,和置於後臺的返回棧。其中,置於後臺的返回棧中所有的Activity都處於stop狀態,用戶可以手動的去切換前後臺的返回棧狀態。
當系統記憶體不足時,系統會優先銷毀處於後臺的Activity。那麼問題來了。後臺銷毀Activity的優先順序是怎樣的呢?是將一個返回棧中的Activity都銷毀了過後,再去銷毀另一個,還是說,只是單純的按照Activity來銷毀回收呢?
1.1 任務管理
任務以及返回棧的管理,可以通過一系列的參數設置來進行,包括我們本文講解的launchMode,也是任務管理的一種方式。
1.1.1 taskAffinity
TaskAffinity即任務相關性,標識一個Activity所需要的返回棧的名字。預設情況下是包名。設置了相同taskAffinity屬性的Activity會被放進同一個棧中。一個返回棧的相關性(affinity)是由這個棧的根Activity的相關性(affinity)決定的。
taskAffinity屬性主要與singleTask或allowTaskReparenting結合使用,在其他情況下,這個屬性沒有作用。這是為什麼呢?
1.1.2 allowTaskReparenting
它的主要作用是Activity的遷移,從一個棧遷移到另一個棧,這個遷移跟Activity的taskAffinity有關。
1.1.3 clearTaskOnLaunch
這個屬性用來清除回退棧中除了根Activity的所有Activity,只對根Activity起作用。當設置為true時,每次重新進入app,只會看到根Activity。
1.1.4 finishOnTaskLaunch
這個屬性與clearTaskOnLaunch相反,它是將本Activity移除出去,而不影響其他的Activity。
1.1.5 alwaysRetainTaskState
這個屬性的作用是保存返回棧的狀態,只對根Activity起作用。正常情況下,系統清理一個返回棧,會將根Activity之上的所有Activity都清除掉。設置該屬性後,系統會保存當前的狀態。
2. 啟動模式
啟動模式主要的作用是什麼呢?根據上面對任務及返回棧的介紹,它的作用是定義,一個新的Activity實例如何與當前的任務相關聯。它本身是任務的管理方式。
啟動模式有兩種定義方式,manifest里定義和intent flag的方式。一種是類似配置式的,一種是代碼層面的。可以大致推測,肯定是帶麽層面的優先順序高一些,但是代碼方式劣處就是不啟動Activity就無法設置。Android中這種一般提供動態以及靜態方式的,套路都大致相同,一些區別各種優劣等。
其中manifest設置與intent flag中都包含對方沒有的方式。這也是兩者的一個區別。
2.1 launchMode
此處的launchMode專指Activity的launchMode屬性。其中有四種方式,這四種方式想必大家也都很清楚了,在這裡我不詳細展開了。
2.1.1 standard
最常見的一種模式,Activity的預設模式,每次啟動該模式的Activity,都會被重新創建,可以從屬不同的任務,也可以在一個任務中被創建多次。
它的應用場景特別廣泛, 一般不是特殊需求的話,都會去使用這種模式。
2.1.2 singleTop
如果在當前任務的棧頂,系統會調用Activity的onNewIntent()方法而不是重新創建一個新的實例。當用戶點擊返回鍵時,當前Activity會被出棧,而不是會退到onNewIntent()之前的狀態。
它的應用場景也有一些。例如搜索頁面,每次打開,搜索一些結果,點擊詳情頁面,然後繼續搜索。在比方說,通過通知打開的頁面,如果該頁面存在,則更新,如果不存在,則創建。
2.1.3 singleTask
該模式只允許系統中存在一個該Activity的實例,如果當前實例不存在,則創建,如果已經存在,則將該實例之上的Activity全部出棧,走onNewIntent()。
singleTask適合作為程式入口點,當通過其他方式調用app時候,不會反覆創建主頁面。例如一般情況下的MainActivity,其他app調用的時候。
2.1.4 singleInstance
這種模式與singleTask十分類似,區別在於,持有該Activity的任務中只能包含一個Activity即它本身。
singleInstance適合需要與程式分離開的頁面,例如鬧鐘的響鈴界面,與鬧鐘的設置相分離。再例如系統的撥號界面。
2.2 Intent flags
此處討論的是通過代碼方式進行設置,常見的有如下三種方式。
2.2.1 FLAG_ACTIVITY_NEW_TASK
使用一個新的返回棧來啟動Activity,跟上面討論的singleTask類似
2.2.2 FLAG_ACTIVITY_SINGLE_TOP
跟上面討論的singleTop類似
2.2.3 FLAG_ACTIVITY_CLEAR_TOP
這種方式是上面討論的launchMode中不存在的,它與singleTop的區別是,當已存在該實例了,會將它之上的Activity都出棧。
它經常與FLAG_ACTIVITY_NEW_TASK組合使用,可以達到singleTask的作用。
3. 回到問題
幾種模式的區別以及應用場景,會有何不同呢?
答案見上面關於launchMode
谷歌是基於什麼原因設計這幾種模式的呢?
關於這個問題,我們先倒著來推理,即從使用場景去考慮,一般狀況下,我們打開一個頁面,不在意是否是唯一,這個是最常見的需求,因此有了standard模式,這種也是預設的模式。當我們用搜索頁面,當最頂層是搜索頁面的時候,我不希望再打開一個搜索頁面,於是有了singleTop模式。當從其他App調用我們的app的時候,我只希望只顯示一個主頁面時,於是有了singleTask。關於singleInstance模式,則是希望與當前的頁面分離。
但是,我覺得谷歌並不能列舉出所有的場景,例如,我希望打開一個頁面,記錄當前的路徑,例如a->b->c,這種場景下,四種模式裡面沒有包含。
如果從正面去推導的話,幾種啟動模式是任務及返回棧的管理。根據在棧中的狀態,大致可以分為如下幾類:
- 最常見的出棧入棧(standard)
- 當前棧中唯一(singleTask)
- 全局唯一(singleInstance)
- 棧頂唯一(singleTop)
是不是很明晰了,有沒有其他的出現形式?肯定有的,例如棧底唯一,棧中唯一。但是這種方式可以等同於當前棧中唯一啊。
我們是否可以推導出,谷歌是根據唯一性,來將啟動模式分為這幾種呢?intent flags則作為輔助的一些操作,例如部分出棧等等。當然這些也只是我的推測,不一定准確,哈哈。
這幾種模式背後的工作原理是什麼呢?
見任務及返回棧
記憶體回收的方式,是以Activity還是以任務作為基準回收?
目前已知的狀況時,如果返回棧置於後臺,當記憶體不足的時候,如果不設置alwaysRetainTaskState屬性的話,會將除了根Activity的所有Activity銷毀掉。可以確定是以返回棧為基準來進行回收。
taskAffinity屬性為什麼與singleTask一起使用才生效?
可以將Activity的launchMode根據是否在棧中唯一分為兩類
- standard、singleTop
- singleTask、singleInstance
第一類因為其唯一性,肯定是與taskAffinity不相容的。singleInstance創建的棧中只能包含本身,預設情況下都會單獨創建一個棧,指定與否都會單獨創建,因此設置沒有意義。而singleTask則是當前棧中唯一,適合作為根Activity,創建一個新的棧,這也是為什麼taskAffinity只能對根Activity起作用的緣故。
4. 最後
我寫的內容不一定正確,一些問題的解釋也是根據我看到的資料來推到出來的,例如Activity為什麼會有四種啟動模式,如果大家有準確地答案,希望告知。另外,文中錯誤的地方,也希望指正。
最後,感謝大家的瀏覽,希望對您有所幫助。