Android中Activity的啓動模式解析

前言

平常我們啓動活動的時候就是直接startActivity或許並沒有注意活動的啓動模式,默認情況下都是以默認的啓動模式啓動。但啓動模式有時候是比較重要的。例如一個活動你想他只啓動一次不要有多個實例,那麼你可能需要把他設置爲singleTask模式。所以有必要了解一下這一些啓動模式。同時要注意一下,啓動模式≠啓動方式,啓動方式是指顯示啓動和隱式啓動,不要混淆,顯示啓動和隱式啓動後續我會有專門的文章講解。

關於任務棧簡介

要了解啓動模式,首先要了解一下關於任務棧的概念。關於任務棧的實現原理等我在這裏就先不說了,這裏主要簡單介紹一下什麼是任務棧。我們啓動的活動實例都會放在一個叫做任務棧的東西里面。我們都知道棧是“後進先出”的特點。打個比方,任務棧就是一個羽毛球筒,活動實例就是一個個羽毛球,後放進去的只能先拿出來。所以當我們啓動一個app的時候,就會自動創建一個任務棧,然後我們就往裏面丟活動實例。當我們按返回銷燬活動的時候,這些活動就依次從任務棧裏面出來。當然,一個app可以擁有多個任務棧,例如使用singleInstence啓動的活動就是在一個獨立的任務棧中。瞭解完任務棧的概念,接下來就可以來看看活動的四種啓動模式。

解析Activity的四種啓動模式

standard

這種是標準啓動模式,默認就是這種啓動模式。每次啓動這種啓動模式的活動的時候都會創建一個新的實例放入棧中,不管棧中是否已經存在相同的實例。這也是最容易理解的。

singleTop

顧名思義,棧頂是單一實例的。什麼意思呢。假設你現在啓動一個ActivityA,但是這個時候已經存在一個ActivityA實例在棧頂,那麼這個時候,就不會創建新的實例。但是如果,在非棧頂存在相同的實例,還是會創建新的實例的。例如,現在棧中的活動是 ABC,A處於棧頂。然後此時啓動A,是不會再創建一個A活動出來,而是執行A的onNewIntent方法;但是如果此時啓動C活動,由於棧頂是A不是C,那麼還是會創建一個新的C實例出來,此時的棧情況就是CABC。

singleTask

單一任務模式。這個模式的意思是,在該活動的啓動棧中,只能存在單一實例,不管是否位於棧頂。與其他啓動模式不同的是,這個啓動模式可以指定棧去啓動。例如現在有一個棧Main,但是你可以給活動A指定一個棧名dev,那麼啓動A的時候就會創建一個棧叫做dev。所以singleTask的意思就是,當你啓動一個啓動模式爲singleTask的活動的時候,如果棧中沒有相同的實例,那麼就會創建一個新的實例放入棧中;如果指定棧中存在相同的實例,例如棧中有ABC,然後你啓動B,那麼這個時候不會去創建新的B實例,而是把B放到棧頂,並把A頂出去,再執行B的onNewIntent方法,此時棧的情況就是BC。
細心的讀者會發現“頂出去”。是的,我們都知道棧是後進先出的特點,例如你往筒裏放了3個羽毛球,那你想要拿到中間那個羽毛球,是不是隻能先把上面那個抽出來呢,同樣的道理,要想把B提到棧頂,那麼必須把A頂出來。可能會有很多讀者誤以爲啓動後是BAC,但其實是BC,因爲A得先出棧,B才能出來。同理,如果棧中是ADFBC,這個啓動B,也是BC,上面的全部被出棧了。

singleInstance

單例模式。這個是singleTask的強化版本。他會自己新建一個棧並把這個新的實例放進去,而且這個棧只能放這個活動實例。所以當重複啓動這個活動的時候,只要他存在,都是調用這個活動onNewIntent方法並切換到這個棧中,並不會去創建新的實例。

設置啓動模式的兩種方法

瞭解了活動的四種啓動模式,接下來看看如何給他指定啓動模式。

靜態設置

靜態設置就是在AndroidManifest中給具體活動設置啓動模式。通過給活動指定launchMode參數來設置啓動模式。例如:

 <activity android:name=".MainActivity"
            android:launchMode="singleInstance"/>

動態設置

動態設置是在啓動活動的時候再指定啓動模式,例如:

Intent intent = new Intent();
intent.setClass(this,SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

可以看到我們通過intent.addFlags這個方法來指定啓動模式,這個方法傳入一個參數來指定啓動模式,其他的參數有:

  • FLAG_ACTIVITY_NEW_TASK:singleTask模式
  • FLAG_ACTIVITY_SINGLE_TOP:singleTop模式
  • FLAG_ACTIVITY_CLEAR_TOP:清除該活動上方的所有活動。一般和singleTask一起使用。但是如果你的啓動模式是standard,那麼這個活動連他之上的所有活動都會被出棧再創建一個新的實例放進去。例如現在棧中是ABCD,以FLAG_ACTIVITY_CLEAR_TOP+standard模式啓動C的時候,首先清理掉ABC,是的,C也會被清理,然後再創建一個新的C放進去,執行之後就是CD。

特別注意的坑

singleInstance返回任務棧

現在模擬一個場景:現在有三個活動 A,B,C。A和C的啓動模式都是standard,B的啓動模式是singleInstance。先啓動A,再啓動B,然後再啓動C。這個時候問題來了,如果我這個時候按下返回鍵,是回到B嗎?答案是回到A。再按一下呢,返回桌面嗎?答案是回到B,再按一下再回到桌面。其實不難理解。我們都知道singleInstance會創建一個獨立的棧,當我們啓動A的時候,A位於棧First中,啓動B的時候,就會創建一個棧Second並把B實例放進去。這個時候再啓動C,就會切換到棧FIrst,因爲singleInstance創建的棧只能放一個,所以C會放到棧First中,當按下返回的時候,棧First中的活動就會依次出棧,直到全部出完,纔會切換到棧Second中。所以要注意這個點。

singleTask多任務棧啓動問題

這個問題和上面singleTop的本質是一樣的。模擬一個場景:現在有兩個棧:First:ABC;Second:QWE。棧First位於前臺,棧Second位於後臺。A位於棧頂。這個時候以singleTask的模式啓動W,會發生什麼樣的情況呢?首先會切換到棧Second,再把Q出棧,W提到棧頂,並執行W的onNewIntent方法。這個時候按返回鍵就會把Second棧中的活動依次出棧,全部出完後纔會切換到棧First。

singleTask的TaskAffinity與allowTaskReparenting參數

前面我們講到給singleTask模式指定要啓動的任務棧的名字,怎麼指定呢?可以在AndroidManifest中指定相關的屬性,如下:

<activity android:name=".Main2Activity"
            android:launchMode="singleTask"
            android:taskAffinity="com.huan"
            android:allowTaskReparenting="true"/>

這裏解釋一下這兩個參數

  • taskAffinity:指定任務棧的名字。默認的任務棧是包名,所以不能以包名來命名。
  • allowTaskReparenting:這個參數表示可不可以切換到新的任務棧,通常設置爲true並和上面的參數一起使用。
    我前面講到可以給singleTask的活動指定一個棧名,然後啓動的時候,就會切換到那個棧,並把新的活動放進去。但是如果設置allowTaskReparenting參數爲false的話是不會切換到新的棧的。這個參數的意思是可不可以把新的活動轉移到新的任務棧。簡單點來說:當我們啓動一個singleTask活動的時候,這個活動還是留在啓動他的活動的棧中的。但是我們指定了taskAffinity這個參數,或者啓動的活動是別的應用中的活動,那麼就會創建一個新的任務棧。如果allowTaskReparenting這個參數是true的話,那麼這個活動就會放到那個新的任務棧中。這樣應該就可以明白了。所以這兩個經常是配套一起使用的。

總結

活動的啓動模式有四種,每種的功能都不一樣,可以結合具體需要去使用,但是最重點還是要了解他的實現原理,棧中是怎麼變化的,這個是比較重要的。瞭解這個之後那些特殊情況也就很容易理解了。
上面我講的只是簡單的使用,關於活動啓動模式還有很多要了解。後續可能會解析一下,讀者也可以自行去深度瞭解。

參考資料

《Android開發藝術探索》 --任玉剛

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章