Activity啓動模式----LaunchMode

四種啓動模式

Standard

標準模式,每當有一次Intent請求,就會建立一個新的Activity實例。html

  • Android 5.0 以前android

    1. 同一應用內git

    新生成的Activity,放入發送Intent者Task的棧頂。github

    TaskRecord{537925a8 #42 A com.zlq.lmt U 0}
         Run #3: ActivityRecord{538314d0 com.zlq.lmt/.StandardActivity}
         Run #2: ActivityRecord{5385a7c4 com.zlq.lmt/.StandardActivity}
         Run #1: ActivityRecord{53760908 com.zlq.lmt/.MainActivity}
    1. 跨應用啓動shell

    新生成的Activity,放入發送Intent者Task的棧的棧頂(儘管他們屬於不一樣的程序,仍是會放入調用者程序的棧內)。網絡

    TaskRecord{537df318 #52 A com.zlq.bbb U 0}
    Run #2: ActivityRecord{537a889c com.zlq.lmt/.StandardActivity}
    Run #1: ActivityRecord{537a4a5c com.zlq.bbb/.MainActivityB}

    這時,咱們打開任務管理器(最近任務按鈕)。會發現最近任務中現實的應用名爲B應用,展現的界面倒是A應用的StandardActivity(由於其位於Task棧頂)。ide

跨應用啓動Standard Activity

  • Android 5.0 以後ui

    1. 同一應用內
      與Android 5.0以前保持一致spa

    2. 跨應用啓動
      經檢驗與Android5.0以前保持一致。Android6.0上也依然沒改變。參考資料深刻講解Android中Activity launchMode內容或許有誤 。.net

  • 使用場景
    standard這種啓動模式適合於撰寫郵件Activity或者社交網絡消息發佈Activity。若是你想爲每個intent建立一個Activity處理,那麼就是用standard這種模式。

SingleTop

棧頂複用模式. SingleTop其實和Standard幾乎同樣,使用SingleTop的Activity也能夠建立不少個實例。惟一不一樣的就是,若是調用的目標Activity已經位於調用者的Task的棧頂,則不建立新實例,而是使用當前的這個Activity實例,並調用這個實例的onNewIntent方法。

在singleTop這種模式下,咱們須要處理應用這個模式的Activity的onCreate和onNewIntent兩個方法,確保邏輯正常。

TaskRecord{537925a8 #42 A com.zlq.lmt U 0}
      Run #4: ActivityRecord{537e3114 com.zlq.lmt/.SingleTopActivity}
      Run #3: ActivityRecord{537dfe7c com.zlq.lmt/.StandardActivity}
      Run #2: ActivityRecord{53770808 com.zlq.lmt/.SingleTopActivity}
      Run #1: ActivityRecord{53760908 com.zlq.lmt/.MainActivity}

棧頂沒法像Standard模式同樣,同事存在兩個,可是整個Task列表中間隔存在多個是能夠的。

SingleTask

棧內複用模式.使用singleTask啓動模式的Activity在一個應用Task中只會存在一個實例。若是這個實例已經存在,intent就會經過onNewIntent傳遞到這個Activity,即屢次調用不會建立新實例。不然新的Activity實例被建立。
狀況包含如下幾種:

  • 同一應用內

  1. 任務棧不存在, 初次啓動SingleTask實例, 會建立任務棧和實例.
    Google在singleTask的文檔有這樣一段描述:

The system creates a new task and instantiates the activity at the root of the new task.

意思爲 系統會建立一個新的Task,並建立Activity實例放入這個新的Task的底部。然而實際並不是如此,在個人例子中,singleTask Activity並建立並放入了調用者所在的Task,而不是放入新的Task:
TaskRecord{5378ff88 #44 A com.zlq.lmt U 0}
      Run #3: ActivityRecord{537e2ff0 com.zlq.lmt/.SingleTaskActivity}
      Run #2: ActivityRecord{537de2ec com.zlq.lmt/.StandardActivity}
      Run #1: ActivityRecord{53788be0 com.zlq.lmt/.MainActivity}
怎樣才能符合文檔中所描述的狀況呢?那就是 `taskAffinity`屬性和singleTask啓動模式配合使用.
<activity
        android:name=".SingleTaskWithTaskAffinityActivity"
        android:label="SingleTaskWithTaskAffinityActivity"
        android:launchMode="singleTask"
        android:taskAffinity="com.zlq.new">
    </activity>

此時再執行一樣的操做,棧內的狀況:

TaskRecord{53778428 #45 A com.zlq.new U 0}
      Run #3: ActivityRecord{537db410 com.zlq.lmt/.SingleTaskWithTaskAffinityActivity}
TaskRecord{5378ff88 #44 A com.zlq.lmt U 0}
      Run #2: ActivityRecord{53760908 com.zlq.lmt/.StandardActivity}
      Run #1: ActivityRecord{53788be0 com.zlq.lmt/.MainActivity}

其實,把啓動模式設置爲singleTask,framework在啓動該activity時只會把它標示爲可在一個新任務中啓動,至因而否在一個新任務中啓動,還要受其餘條件的限制。使用taskAffinity屬性會指定新的Activity所屬棧,可與SingleTask配合使用, 對Standard模式無效.新任務棧是com.zlq.new.

  1. 任務棧存在, 初次啓動SingleTask實例, Task棧中不存在singleTask Activity的實例。那麼就須要建立這個Activity的實例,而且將這個實例放入和調用者相同的Task中並位於棧頂。與Standard模式相同.

  2. 任務棧相同,若是singleTask Activity實例已然存在,再次啓動SingleTask實例, 那麼在Activity回退棧中,全部位於該Activity上面的Activity實例都將被銷燬掉(銷燬過程會調用Activity生命週期回調),這樣使得singleTask Activity實例位於棧頂(具備clearTop的效果)。與此同時,Intent會經過onNewIntent傳遞到這個SingleTask Activity實例。 並清除其上面實例, 具備clearTop的效果.最終,singleTask Activity實例會位於棧頂。

  3. 任務棧不一樣, 再次啓動SingleTask實例, 會致使任務棧切換, 後臺置於前臺.

  • 跨應用之間:

  1. 任務棧不存在, 初次啓動SingleTask實例, 會建立一個新的任務棧,而後建立SingleTask Activity的實例,將其放入新的Task中。Task變化以下。

TaskRecord{5bf28 #16 A=com.zlq.bbb U=0 sz=1}
        Run #0: ActivityRecord{f4a1b15 u0 com.zlq.bbb/.MainActivityB t16}

變爲:

TaskRecord{5c70a93 #17 A=com.zlq.lmt U=0 sz=1}
        Run #1: ActivityRecord{4cd8b0f u0 com.zlq.lmt/.SingleTaskActivity t17}
TaskRecord{5bf28 #16 A=com.zlq.bbb U=0 sz=1}
        Run #0: ActivityRecord{f4a1b15 u0 com.zlq.bbb/.MainActivityB t16}

最近任務變化:
此處輸入圖片的描述
變爲:
此處輸入圖片的描述

  1. 任務棧存在, 初次啓動SingleTask實例, Task棧中不存在singleTask Activity的實例。
    若是singleTask Activity所在的應用進程存在,可是singleTask Activity實例不存在,那麼從別的應用啓動這個Activity,新的Activity實例會被建立,並放入到所屬進程所在的Task中,並位於棧頂位置。

Running activities (most recent first):
TaskRecord{5bf28 #16 A=com.zlq.bbb U=0 sz=1}
        Run #1: ActivityRecord{f4a1b15 u0 com.zlq.bbb/.MainActivityB t16}
TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=1}
        Run #0: ActivityRecord{f0eba63 u0 com.zlq.lmt/.MainActivity t18}

↓變爲↓:

Running activities (most recent first):
    TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=2}
        Run #2: ActivityRecord{73d091c u0 com.zlq.lmt/.SingleTaskActivity t18}
    TaskRecord{5bf28 #16 A=com.zlq.bbb U=0 sz=1}
        Run #1: ActivityRecord{f4a1b15 u0 com.zlq.bbb/.MainActivityB t16}
    TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=2}
        Run #0: ActivityRecord{f0eba63 u0 com.zlq.lmt/.MainActivity t18}
  1. 若是singleTask Activity實例存在,從其餘程序被啓動,那麼這個Activity所在的Task會被移到頂部,而且在這個Task中,位於singleTask Activity實例之上的全部Activity將會被正常銷燬掉。若是咱們按返回鍵,那麼咱們首先會回退到這個Task中的其餘Activity,直到當前Task的Activity回退棧爲空時,纔會返回到調用者的Task。

Running activities (most recent first):
      TaskRecord{5bf28 #16 A=com.zlq.bbb U=0 sz=1}
        Run #4: ActivityRecord{f4a1b15 u0 com.zlq.bbb/.MainActivityB t16}
      TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=4}
        Run #3: ActivityRecord{fa7aae9 u0 com.zlq.lmt/.StandardActivity t18}
        Run #2: ActivityRecord{dfd9a3b u0 com.zlq.lmt/.StandardActivity t18}
        Run #1: ActivityRecord{660ce3c u0 com.zlq.lmt/.SingleTaskActivity t18}
        Run #0: ActivityRecord{f0eba63 u0 com.zlq.lmt/.MainActivity t18}

↓變爲↓:

Running activities (most recent first):
      TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=2}
        Run #2: ActivityRecord{660ce3c u0 com.zlq.lmt/.SingleTaskActivity t18}
      TaskRecord{5bf28 #16 A=com.zlq.bbb U=0 sz=1}
        Run #1: ActivityRecord{f4a1b15 u0 com.zlq.bbb/.MainActivityB t16}
      TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=2}
        Run #0: ActivityRecord{f0eba63 u0 com.zlq.lmt/.MainActivity t18}

能夠看到,TASK ID爲#18 的任務棧已經從原來的4個變爲最終的1+1個。

  • 使用場景:
    該模式的使用場景多相似於郵件客戶端的收件箱或者社交應用的時間線Activity。上述兩種場景須要對應的Activity只保持一個實例便可,可是也要謹慎使用這種模式,由於它能夠在用戶未感知的狀況下銷燬掉其餘Activity。

SingleInstance

單實例模式啓動時, 系統會爲其創造一個單獨的任務棧, 之後每次使用, 都會使用這個單例, 直到其被銷燬, 屬於真正的單例模式.singleTask差很少,惟一不一樣的就是存放singleInstance Activity實例的Task只能存放一個該模式的Activity實例,不能有任何其餘的Activity。
雖然是兩個task,可是在系統的任務管理器中,卻始終顯示一個,即位於頂部的Task中。

相關知識點

查看當前任務棧:

adb shell dumpsys activity | sed -n -e '/Stack #/p' -e '/Running activities/,/Run #0/p'

輸出的結果如:

Running activities (most recent first):
      TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=2}
        Run #2: ActivityRecord{660ce3c u0 com.zlq.lmt/.SingleTaskActivity t18}
      TaskRecord{5bf28 #16 A=com.zlq.bbb U=0 sz=1}
        Run #1: ActivityRecord{f4a1b15 u0 com.zlq.bbb/.MainActivityB t16}
      TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=2}
        Run #0: ActivityRecord{f0eba63 u0 com.zlq.lmt/.MainActivity t18}

TaskRecord{65dfdf0 #18 A=com.zlq.lmt U=0 sz=2}爲例
以TaskRecord開頭的(如)爲一組 TaskRecord記錄,#18爲Task的ID,A=包名,sz爲該Task的Activity數量。
以Run #開頭的爲一個ActivityRecord記錄。其中也包含了包名、類名、TASK ID等信息。

其中以Task ID爲一個任務棧的惟一標識,ID相同的TaskRecord屬於同一個任務棧(能夠理解爲同一應用)。

對startActivityForResult的影響:

startActivityForResult 不一樣於 startActivity, 在使用 startActivityForResult 時無論LaunchMode設置爲哪一種模式,都會在調用者Task棧中新建實例以正確地返回數據。在棧中的展示形式均與Standard相同(可生成多份連續的實例)。

  • SingleTop,當其使用startActivityForResult時表現和Standard啓動模式時徹底相同

  • SingleTask,當不定義 taskAffinity 屬性時使用startActivityForResult和Standard啓動模式時表現徹底相同。當定義了taskAffinity 屬性後,變現將和下面第3條表現一致。

  • SingleInstance,沒法經過startActivity建立本身(不管當前所屬哪一個棧)。startActivityForResult 隨意在當前棧(傳入者所在棧)新建實例。

Running activities (most recent first):
      TaskRecord{ef9f20b #33 A=com.zlq.lmt U=0 sz=4}
        Run #3: ActivityRecord{34f60b1 u0 com.zlq.lmt/.SingleTaskActivity t33} *
        Run #2: ActivityRecord{914547d u0 com.zlq.lmt/.SingleTaskActivity t33} *
        Run #1: ActivityRecord{a1f3e09 u0 com.zlq.lmt/.SingleTaskActivity t33}
        Run #0: ActivityRecord{f7db1c u0 com.zlq.lmt/.MainActivity t33}

上面代碼片斷中,加了*標的表示使用startActivity沒法創建,是 使用startActivityForResult 創建的Activity。
由此可知, 由於startActivityForResult須要返回值, 會保留實例, 部分覆蓋單例效果.

注意: 4.x版本經過startActivityForResult啓動singleTask, 沒法正常獲取返回值, 參考.
5.x以上版本修復此問題, 考慮兼容性, 不推薦使用startActivityForResult和singleTask.

Demo源碼

以上均爲參考資料和本身實踐驗證所得結果。有描述不清楚的地方,你們可去下載個人代碼自行驗證各類狀況:GitHub

參考資料連接

深刻講解Android中Activity launchMode
分析 Activity 的啓動模式
Android中Activity四種啓動模式和taskAffinity屬性詳解

相關文章
相關標籤/搜索