1. 程式人生 > >Activity啟動模式之SingleInstance

Activity啟動模式之SingleInstance

啟動模式系列之:Activity啟動模式(Standrd和SingleTop)
啟動模式系列之:Activity啟動模式(SingleTask)
啟動模式系列之:Activity啟動模式(SingleInstance)

一,SingleInstance模式(單例項模式)

解釋:在啟動單例項模式的Activity時,系統為其建立一個耽誤的任務棧,以後每次都會使用這個單例,直到這個單例被摧毀。

與棧內複用模式不同,棧內複用模式是指定taskAffinity屬性,所建立的任務棧並不獨享,仍可新增其他的Activity,而單例項模式所建立的任務棧只含有這個例項。所以說是真正的單例模式。

二,不設定taskAffinity屬性。

操作示例:

MainActivity -> TestAActivity -> TestBActivity -> TestCActivity

adb記錄:

  Stack #1:
    Task id #622
      TaskRecord{9d34a08 #622 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=3}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity }
        Hist #2: ActivityRecord{d8c8249 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestCActivity t622}
          Intent { flg=0x10400000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestCActivity }
          ProcessRecord{1d3f65bb 23767:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}
        Hist #1: ActivityRecord{1d98d9ee u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestAActivity t622}
          Intent { cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestAActivity }
          ProcessRecord{1d3f65bb 23767:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}
        Hist #0: ActivityRecord{c7511f7 u0 org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity t622}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity }
          ProcessRecord{1d3f65bb 23767:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}
    Task id #623
      TaskRecord{19b525a1 #623 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=1}
      Intent { flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity }
        Hist #0: ActivityRecord{1fb47bb4 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity t623}
          Intent { flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity }
          ProcessRecord{1d3f65bb 23767:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}

    Running activities (most recent first):
      TaskRecord{9d34a08 #622 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=3}
        Run #3: ActivityRecord{d8c8249 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestCActivity t622}
      TaskRecord{19b525a1 #623 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=1}
        Run #2: ActivityRecord{1fb47bb4 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity t623}
      TaskRecord{9d34a08 #622 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=3}
        Run #1: ActivityRecord{1d98d9ee u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestAActivity t622}
        Run #0: ActivityRecord{c7511f7 u0 org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity t622}

    mResumedActivity: ActivityRecord{d8c8249 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestCActivity t622}

首先,操作上此時的TestCActivity應該位於棧頂,從日誌上也可以看出來,的確如此

再次,因為TestBActivity是SingleInstance模式,所以它在一個單獨的任務棧中。從日誌中可以看出來,TestBActivity確實在單獨的任務棧TaskTecord中,但是這個任務棧的A值和系統預設任務棧的A值一樣,都是包名,但是Task id不一樣。這一點和我們的棧內複用設定taskAffinity不太一樣,但是也是兩個任務棧。

繼續操作:

關閉TestCActivity

adb記錄:

  Stack #1:
    Task id #622
      TaskRecord{9d34a08 #622 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=2}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity }
        Hist #1: ActivityRecord{1d98d9ee u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestAActivity t622}
          Intent { cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestAActivity }
          ProcessRecord{1d3f65bb 23767:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}
        Hist #0: ActivityRecord{c7511f7 u0 org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity t622}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity }
          ProcessRecord{1d3f65bb 23767:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}
    Task id #623
      TaskRecord{19b525a1 #623 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=1}
      Intent { flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity }
        Hist #0: ActivityRecord{1fb47bb4 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity t623}
          Intent { flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity }
          ProcessRecord{1d3f65bb 23767:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}

    Running activities (most recent first):
      TaskRecord{9d34a08 #622 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=2}
        Run #2: ActivityRecord{1d98d9ee u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestAActivity t622}
      TaskRecord{19b525a1 #623 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=1}
        Run #1: ActivityRecord{1fb47bb4 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity t623}
      TaskRecord{9d34a08 #622 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=2}
        Run #0: ActivityRecord{c7511f7 u0 org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity t622}

    mResumedActivity: ActivityRecord{1d98d9ee u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestAActivity t622}

從日誌我們可以看出來,此時位於棧頂的的是TestActivity而不是,啟動TestCActivity的TestBActivity。因為TestB位於自己的單獨任務棧中,而MainActiity,TestAActivity,TestCActivity位於一個任務棧中。此時TestCActivity出棧,那麼會切換到TestC所在的任務棧中,並顯示這個棧中的棧頂Activity。

繼續操作:

關閉TestAActivity,關閉MainActivity。

adb記錄如下:

  Stack #1:
    Task id #623
      TaskRecord{19b525a1 #623 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=1}
      Intent { flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity }
        Hist #0: ActivityRecord{1fb47bb4 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity t623}
          Intent { flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity }
          ProcessRecord{1d3f65bb 23767:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}

    Running activities (most recent first):
      TaskRecord{19b525a1 #623 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=1}
        Run #0: ActivityRecord{1fb47bb4 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity t623}

    mResumedActivity: ActivityRecord{1fb47bb4 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity t623}

理論上,我們推出MainActivity頁面應該退回到桌面,但是此時我們能看到。其實是回到了TestBActivity。

以上我們可以得到一個結論就是,當呼叫到單例項的任務棧,單例項的任務棧(唯一)在調起其他Activity(進入系統預設任務棧)。在回退的時候,會優先退出當前頁面所在的任務棧,然後在退出單例項的任務棧。

第二種操作:

MainActivity -> TestAActivity -> TestBActivity -> TestCActivity -> TestBActivity

adb記錄如下:

  Stack #1:
    Task id #627
      TaskRecord{3daa3fc3 #627 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=1}
      Intent { flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity }
        Hist #0: ActivityRecord{3a2df81 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity t627}
          Intent { flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity }
          ProcessRecord{3715cf72 28186:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}
    Task id #626
      TaskRecord{34bcc040 #626 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=3}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity (has extras) }
        Hist #2: ActivityRecord{285247f3 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestCActivity t626}
          Intent { flg=0x10400000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestCActivity }
          ProcessRecord{3715cf72 28186:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}
        Hist #1: ActivityRecord{19c94413 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestAActivity t626}
          Intent { cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestAActivity }
          ProcessRecord{3715cf72 28186:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}
        Hist #0: ActivityRecord{29ed325 u0 org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity t626}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity (has extras) }
          ProcessRecord{3715cf72 28186:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}

    Running activities (most recent first):
      TaskRecord{3daa3fc3 #627 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=1}
        Run #3: ActivityRecord{3a2df81 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity t627}
      TaskRecord{34bcc040 #626 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=3}
        Run #2: ActivityRecord{285247f3 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestCActivity t626}
        Run #1: ActivityRecord{19c94413 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestAActivity t626}
        Run #0: ActivityRecord{29ed325 u0 org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity t626}

    mResumedActivity: ActivityRecord{3a2df81 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity t627}

從上面日誌我們可以看到。TestBActivity位於棧頂。而從Activity的生命週期日誌我們也應該知道。TestC再次調起TestB。此時TestB並沒有重複建立,而是複用的他單獨任務棧的中例項。並將該例項至於棧頂。而此時就再次出現了。應用預設任務棧和單例項任務棧的再次前後臺切換。

三,設定taskAffinity屬性。

操作記錄:

MainActivity -> TestAActivity -> TestBActivity -> TestCActivity

adb記錄如下:

 Stack #1:
    Task id #637
      TaskRecord{3de69dbf #637 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=3}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity }
        Hist #2: ActivityRecord{d69a4a6 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestCActivity t637}
          Intent { flg=0x10400000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestCActivity }
          ProcessRecord{2c4937de 31071:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}
        Hist #1: ActivityRecord{2430b46 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestAActivity t637}
          Intent { cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestAActivity }
          ProcessRecord{2c4937de 31071:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}
        Hist #0: ActivityRecord{71407be u0 org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity t637}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity }
          ProcessRecord{2c4937de 31071:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}
    Task id #638
      TaskRecord{1ffb8c8c #638 A=org.wangchenlong.stack U=0 sz=1}
      Intent { flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity }
        Hist #0: ActivityRecord{9216615 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity t638}
          Intent { flg=0x10000000 cmp=org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity }
          ProcessRecord{2c4937de 31071:org.wangchenlong.wcl_activity_launchmode_demo/u0a460}

    Running activities (most recent first):
      TaskRecord{3de69dbf #637 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=3}
        Run #3: ActivityRecord{d69a4a6 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestCActivity t637}
      TaskRecord{1ffb8c8c #638 A=org.wangchenlong.stack U=0 sz=1}
        Run #2: ActivityRecord{9216615 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestBActivity t638}
      TaskRecord{3de69dbf #637 A=org.wangchenlong.wcl_activity_launchmode_demo U=0 sz=3}
        Run #1: ActivityRecord{2430b46 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestAActivity t637}
        Run #0: ActivityRecord{71407be u0 org.wangchenlong.wcl_activity_launchmode_demo/.MainActivity t637}

    mResumedActivity: ActivityRecord{d69a4a6 u0 org.wangchenlong.wcl_activity_launchmode_demo/.TestCActivity t637}

從日誌上我們可看出來,還是TestBActivity位於一個單獨的任務棧,其他位於一個任務棧,不同於不設定taskAffinity屬性的是。設定以後的TaskRecord的A值是我們設定的taskAffinity的值。其他跟預設不設定沒有區別。有興趣的同學可以試一下。

四,Activity啟動模式總結

Android系統的任務棧體系分為三層:

  1. Stack,每一個應用屬於一個Stack。一一對應
  2. TaskRecord,它含有一組Activity,通過單例模式SingleTask,SingleInstance支援建立多個TaskRecord。
  3. ActivityRecord,每一個Activity例項就是一個ActivityRecord。相同的支援存在多份例項。

啟動模式本質上是為了避免相同Activity建立多個例項。避免系統資源的浪費。因此Android系統通過啟動模式提供三種解決方案:

  1. SingleTop棧頂複用模式。當Activity位於棧頂時,直接複用。當位於棧中的時候,和標準模式一樣,需要重複建立。
  2. SingleTask棧內複用模式。當Activity位於棧頂時,直接複用。當位於棧中的時候,清除其上的全部例項,直接複用,最終其就位於棧頂了。
  3. SingleInstance單例項模式,直接建立一個任務棧,無論Activity在什麼位置。都會複用這個例項。

需要注意的是。SingleTask和SingleInstance都支援建立新的任務棧,這會涉及到前臺任務棧和後臺任務棧的關係。即一個Stack中有多個TaskRecord。

如果使用系統預設的後腿操作,即出棧操作,只有當前任務棧全部出棧,才會切換到其他任務棧,或者通過啟動單例項模式的Activity才會觸及切換任務棧。

另外還有一點,當單例項的任務棧位於系統預設任務棧上面的時候,此時如果點選手機Home鍵到桌面,然後再次點選應用圖示,系統可能會認為是再次啟動,直接切換到系統預設任務棧,此時顯示的是系統任務棧棧頂的那個頁面,而在退出這個棧所有的頁面以後,並沒有自動切換到單例項的任務棧,而是直接到桌面了。單例項的任務棧此時還在記憶體中。如果再次啟動應用呼叫單例項的頁面,會複用該頁面並切換到單例項的任務棧中。

Demo下載:https://download.csdn.net/download/wangxp423/10767068

參考:《高階Android開發強化實戰》

推薦:https://github.com/wangxp423 喜歡的小夥伴點一下 Star