《Android深入透析》之 淺析Activity啟動模式


前言:

      Activity的啟動模式是一個既基礎又容易忽視的問題,但是這個問題有個深刻的認識,對程序員寫一個穩定高效的Android程序幫助很大,今天,在B哥引導下,我們對Activity啟動模式、Intent Flags做了一番很好的探究,可以這么說,如果你不熟悉或了解Activity的啟動模式或者Flags怎么用,今后你在實際開發中,絕對會被困擾,回過頭來重新學習這一節,舉個例子:有人寫出的客戶端,為什么崩潰了,底下仍然有一個乃至N個該應用的界面,如果你熟讀並且准確理解此章,必然不會出此錯誤。

探究歷程:

     ①   什么是棧

     ②   Activity棧

     ③   Task

     ④  Activity啟動模式

     ⑤  Activity棧和Task聯系

     ⑥  Intent Flags 

     ⑦  Activity相關屬性taskAffinity

 1.    什么是棧

1.1   

       棧是一種常用的數據結構,棧只允許訪問棧頂的元素,棧就像一個子彈梭子(如圖所示),每次都只能取棧頂上的東西,而對於棧就只能每次訪問它的棧頂元素,從而可以達到保護棧頂元素以下的其他元素.”先進后出”或”后進先出”就是棧的一大特點,先進棧的元素總是要等到后進棧的元素出棧以后才能出棧.遞歸就是利用到了系統棧,暫時保存臨時結果,對臨時結果進行保護.

 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM