Activity的啓動模式是一個既基礎又容易忽視的問題,可是這個問題有個深入的認識,對程序員寫一個穩定高效的Android程序幫助很大,今天,在B哥引導下,咱們對Activity啓動模式、Intent Flags作了一番很好的探究,能夠這麼說,若是你不熟悉或瞭解Activity的啓動模式或者Flags怎麼用,從此你在實際開發中,絕對會被困擾,回過頭來從新學習這一節,舉個例子:有人寫出的客戶端,爲何崩潰了,底下仍然有一個乃至N個該應用的界面,若是你熟讀而且準確理解此章,必然不會出此錯誤。android
① 什麼是棧程序員
② Activity棧微信
③ Task數據結構
④ Activity啓動模式app
⑤ Activity棧和Task聯繫學習
⑥ Intent Flags spa
⑦ Activity相關屬性taskAffinityxml
1. 什麼是棧對象
1.1 棧blog
棧是一種經常使用的數據結構,棧只容許訪問棧頂的元素,棧就像一個子彈梭子(如圖所示),每次都只能取棧頂上的東西,而對於棧就只能每次訪問它的棧頂元素,從而能夠達到保護棧頂元素如下的其餘元素.」先進後出」或」後進先出」就是棧的一大特色,先進棧的元素老是要等到後進棧的元素出棧之後才能出棧.遞歸就是利用到了系統棧,暫時保存臨時結果,對臨時結果進行保護.
1.2 定義棧(Stack)
棧的定義棧(Stack)是限制僅在表的一端進行插入和刪除運算的線性表。
(1) 一般稱插入、刪除的這一端爲棧頂(Top),另外一端稱爲棧底(Bottom)。
(2) 當表中沒有元素時稱爲空棧。
(3) 棧爲後進先出(Last In First Out)的線性表,簡稱爲LIFO表。棧的修改是按後進先出的原則進行。每次刪除(退棧)的老是當前棧中"最新"的元素,即最後插入(進棧)的元素,而最早插入的是被放在棧的底部,要到最後才能彈出。
1.3 棧的操做:壓棧、彈棧
2. Activity中的棧
Android的管理主要是經過Activity棧來進行,當一個Activity啓動時,系統會根據其配置將它壓入到一個特定的棧中,系統處於運行狀態。當用戶點擊返回或則FINISH()了該Activity,那麼它便會被從棧中壓出,隨之摧毀,按照Activity的生命週期能夠知道,若是當前顯示的棧中的Activity沒有被彈出棧(即調用Activity的ondestory方法),那麼經過Intent打開一個新的Activity時候,會將新打開的Activity壓入到棧頂. 以下 圖(1)
圖(1)
3. Task
Task就是Activity的任務棧,它簡單的說,就是一組以棧的模式彙集在一塊兒的Activity組件集合,記錄着用戶的行爲。(這裏只提它和Activity的啓動模式來說)
4. Activity啓動模式
屬性:android:launchMode
做用:經過主配置文件AndroidManifest.xml中activity的launchMode 屬性決定Activity如何啓動。
描述:這裏有四種模式,與Intent對象中的Activity Flags的屬性(FLAG_ACTIVITY_*變量)共同做用,來決定Activity如何啓動來處理Intent。
四種模式:
① "standard" --默認模式
② "singleTop"
③ "singleTask"
④ "singleInstance"
如下舉例說明它們的區別:
A. standard:
Activity的默認加載方法,該方法會啓動建立一個新的activity實例,同時將該實例壓入到棧中(無論該activity是否已經存在在Task棧中,都是採用new操做),並調用這個新Activity的OnCreate()方法。。
例如: 棧中順序是A B C D ,此時D經過Intent跳轉到A,那麼棧中結構就變成 A B C D A ,點擊返回按鈕的 顯示順序是 D C B A,依次摧毀。以下圖(2)
一句話記憶:不管棧中是否已經建立過,它都會建立一個新的並置於棧頂並顯示,調用oncreate方法。
使用場景:默認的Activity的啓動模式。
圖(2)
B. singleTop:
假設棧內已存在A B C D
singleTop模式下,分如下兩種狀況。
1) 當前App應用展現的是D或者棧頂是D,若是這個時候經過Intent須要打開D,那麼不會從新建立一個新的D實例,而是調用棧頂的D,並調用它的onNewIntent方法(注意不是oncreate方法,只有建立一個新的實例Activity纔會調用Oncreate方法)因此棧中的結構依舊爲A B C D。以下圖(3)
圖(3)
思考: 那麼,讓咱們想一想,這種狀況何時會發生呢?好像有點矛盾,當前的顯示的或者棧頂的activity,經過Intent或者打開一樣的activity,有這種用法和必要嗎?
古語有云:萬物皆有其法,android提供了這種用法,那麼必定有其道理,至於如何應用和怎樣應用,那麼有待讀者驗證,在這裏咱們只是拋磚引玉,不過能夠提供一個這樣的場景論證其有效性。
好比:當前的app棧頂爲A,這時候,忽然來了個消息通知,這個消息通知須要經過Intent去打開A,那麼咱們點擊這個消息通知打開A的時候,此場景就復現了,它將不會建立一個新的A,而是複用棧頂的A,並執行onNewIntent方法。
2) 當前App應用展現的是D或者棧頂是D,若是這個時候須要打開B,那麼因爲B不處於棧頂,因此會新建一個B實例並壓入到棧中,並調用新建立B的onCreate方法,結構就變成了A B C D B。以下圖(4)
圖(4)
一句話記憶:只有須要打開的A在棧頂,那麼不會建立一個新的A,並調用onNewIntent方法,若是須要打開的A不在棧頂,那麼不論A在棧中有仍是沒有,都會建立一個新的A放入棧頂,並執行onCreate方法。
C. singleTask:
singleTask模式下,Task棧中有且只能有一個對應Activity的實例。
例如:當前棧的結構爲:A B C D。
此時D經過Intent打開B,則棧的結構變成了:A B。其中的C和D被棧彈出銷燬了,也就是說位於B之上的實例都被銷燬了。而不會建立一個新的B實例,而是使用棧中原有的B,此時調用原B的onNewIntent()方法。以下圖(5),(6)
圖(5)
圖(6)
經驗談:此模式較爲常見,在activity棧中,由於應用須要,一般須要打開多個相同或者不一樣的Activity,那麼這樣,Activity棧會愈來愈大,從而消耗的內存也會愈來愈大,若是Activity配置了這個屬性就無敵了,它會怎麼作呢?若是在已經打開A後打開了B C D E…等等,若是這個時候你須要打開A,但又想銷燬B C D E…的時候,此屬性就知足需求了,這時候你會發現你內存使用就降低了,由於B C D E…已經被銷燬。
經驗談:固然,並非說設置了singleTask就通用一切了,前面說過了,每種用法都有其自身道理,singleTask想作到的是,顯示當前堆棧中已存在的Activity並不從新建立,而是複用,若是堆棧中已存在須要打開的Activity,那麼先將此棧中Activity之上的其它Activity銷燬,露出已存在的Activity,並執行它的onNewIntent方法。
固然,若是當前棧中不存在,那就建立一個新的置於棧頂。
一句話記憶:只要A存在棧內,那麼就將A之上的所有銷燬(不包含A),同時顯示並複用A,執行onNewIntent方法。不然,建立一個新A置於棧頂。
D. singleInstance:
singleInstance模式下,會建立一個新的Task棧。
例如:當前棧的結構爲:A B C D。
在D中經過Intent打開E(E的模式爲singleInstance),那麼會新建一個Task 棧2,棧Task 1中結構依舊爲A B C D,棧2中結構爲E,此時屏幕中顯示E。
此類模式,一般會在打開另外一個App纔會使用。好比:打電話,使用平率高,耗資源的應用。在應用中打開微信、新浪微博等客戶端。以下圖(7)
圖(7)
5. Activity棧和Task聯繫
Task簡單的就是一組以棧的模式彙集在一塊兒的Activity組件集合,相似於一個填充了Activity的容器,最早加入的Activity會處於容器最下面,最後加入的處於容器最上面,而從Task中取出Activity是從最頂端先取出,最後取出的是最開始添加Activity,這就是後進先出(Last In First Out)模式,而Activity在Task中的順序是能夠控制的,在Activity跳轉時用到Intent Flag能夠設置新建activity的建立方式(這裏就涉及到了Intent Flag的使用)。
6. Intent Flags
Flags: 表示Intent的標誌位,經常使用於Activity的場景中,它和Activity的啓動模式有着密切的聯繫,簡單說,flag的有效組合(一般用「|「組合使用)決定如何打開Activity。
下面列舉的是和本文主題相關的Flags屬性:
(1)—— Intent.FLAG_ACTIVITY_NEW_TASK (默認)
默認的跳轉類型,它會從新建立一個新的Activity。
(2)—— FLAG_ACTIVITY_SINGLE_TOP
這個FLAG就至關於啓動模式中的singletop,請參考singletop說明。
(3)—— FLAG_ACTIVITY_CLEAR_TOP
這個FLAG啓動的Activity會把要啓動的Activity之上(包含自身)的Activity所有彈出棧空間。例如:原來棧中的結構是A B C D ,從D中跳轉到B,釋放順序爲D C B,而後從新建立B置於棧頂,棧中的結構就變爲了A B了。(這個方法能夠用來關閉多個Activity)
經驗:須要銷燬棧中A以前的多個activity,但並不想銷燬A,就須要FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_SINGLE_TOP組合使用
(4)—— FLAG_ACTIVITY_CLEAR_TASK
這個Flag使用的前置條件爲:API level 11 以上版本,而且須要與FLAG_ACTIVITY_NEW_TASK一塊兒使用。
此標識,用於釋放當前棧中全部的activity,而後再建立新的Activity對象置於棧頂。
例如棧中原有A B C D,須要從D跳轉到B,依次釋放D C B A,而後建立B,置於棧頂。
7. Activity相關屬性taskAffinity
Activity 中的 android:taskAffinity 這個屬性介紹:
Activity爲Task擁有的一個affinity。擁有相同的affinity的Activity理論上屬於相同的Task(在用戶的角度是相同的「應用程序」)。Task的affinity是由它的根Activity決定的。
affinity決定兩件事情——Activity從新宿主的Task(參考allowTaskReparenting特性)和使用FLAG_ACTIVITY_NEW_TASK標誌啓動的Activity宿主的Task。
默認狀況,一個應用程序中的全部Activity都擁有相同的affinity。捏能夠設定這個特性來重組它們,甚至能夠把不一樣應用程序中定義的Activity放置到相同的Task中。爲了明確Activity不宿主特定的Task,設定該特性爲空的字符串。
若是這個特性沒有設置,Activity將從應用程序的設定那裏繼承下來(參考<application>元素的taskAffinity特性)。應用程序默認的affinity的名字是<manifest>元素中設定的package名。
android:taskAffinity只有經過標誌位爲FLAG_ACTIVITY_NEW_TASK的Intent啓動Activity時,該Activity的這個屬性纔會生效,系統纔會將具備相同Task親和力的Task切換到前臺,而後啓動該Activity,不然該Activity仍然運行在啓動它的Task中。