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系統的任務棧體系分為三層:
- Stack,每一個應用屬於一個Stack。一一對應
- TaskRecord,它含有一組Activity,通過單例模式SingleTask,SingleInstance支援建立多個TaskRecord。
- ActivityRecord,每一個Activity例項就是一個ActivityRecord。相同的支援存在多份例項。
啟動模式本質上是為了避免相同Activity建立多個例項。避免系統資源的浪費。因此Android系統通過啟動模式提供三種解決方案:
- SingleTop棧頂複用模式。當Activity位於棧頂時,直接複用。當位於棧中的時候,和標準模式一樣,需要重複建立。
- SingleTask棧內複用模式。當Activity位於棧頂時,直接複用。當位於棧中的時候,清除其上的全部例項,直接複用,最終其就位於棧頂了。
- SingleInstance單例項模式,直接建立一個任務棧,無論Activity在什麼位置。都會複用這個例項。
需要注意的是。SingleTask和SingleInstance都支援建立新的任務棧,這會涉及到前臺任務棧和後臺任務棧的關係。即一個Stack中有多個TaskRecord。
如果使用系統預設的後腿操作,即出棧操作,只有當前任務棧全部出棧,才會切換到其他任務棧,或者通過啟動單例項模式的Activity才會觸及切換任務棧。
另外還有一點,當單例項的任務棧位於系統預設任務棧上面的時候,此時如果點選手機Home鍵到桌面,然後再次點選應用圖示,系統可能會認為是再次啟動,直接切換到系統預設任務棧,此時顯示的是系統任務棧棧頂的那個頁面,而在退出這個棧所有的頁面以後,並沒有自動切換到單例項的任務棧,而是直接到桌面了。單例項的任務棧此時還在記憶體中。如果再次啟動應用呼叫單例項的頁面,會複用該頁面並切換到單例項的任務棧中。
Demo下載:https://download.csdn.net/download/wangxp423/10767068
參考:《高階Android開發強化實戰》
推薦:https://github.com/wangxp423 喜歡的小夥伴點一下 Star