《Android深刻透析》之 淺析Activity啓動模式

前言:

      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中。

相關文章
相關標籤/搜索