標籤:
總結:
- 同一task內的activity可以是來自不同進程的activity
- 棧內的activity不會重新排序,只能push或者pop
- standard模式允許多執行個體,可以在不同的task
- singleTask的activity只會存在一個執行個體
- singleTask的activity如果設定了獨立的taskAffinity屬性值,啟動時就會在新的task中,否則會在已有task中
- singleTask的activity啟動時,它會在目標task(新的task或者已有task)中查看是否已經存在相應的activity執行個體,如果存在,就會把位於這個activity執行個體上面的activity全部銷毀(pop, destroy)掉,即最終這個activity執行個體會位於任務的堆棧頂端中
task
task是一個從使用者角度出發的概念,它是一些activity的組合,它們組合起來是為了讓使用者完成某一件工作(或者說操作)。
task內的activity們以棧的形式組織起來,也就是back stack了。棧內的activity不會重新排序,只能push或者pop。棧內的activity可以來自不同的app,因此可以是運行在不同的進程,但是它們都屬於同一個task內。
安卓系統是即時多task系統,使用者可以隨意在多個task之間切換。當一個task的棧內所有activity都pop之後,task也就銷毀了。有時系統為了回收記憶體,會銷毀activity,但是task不會銷毀。
activity在manifest中有launchMode選項,可以配置其啟動的模式。標準模式下,允許一個activity同時存在多個執行個體,既可以在同一個task,也可以在不同task。manifest可以指定launchMode,intent可以指定intent flag,前者供被啟動者使用,後者供啟動者使用,同時使用時後者會覆蓋前者。
launchMode
- standard,預設模式,允許多執行個體
- singleTop,相比於standard,有新的啟動請求時,只有在目標activity處於當前棧頂時,才會調用
onNewIntent()而不建立新執行個體,其他情況都和standard一致
- singleTask,(這段摘自老羅的部落格)設定了”singleTask”啟動模式的Activity,它在啟動的時候,會先在系統中尋找屬性值affinity等於它的屬性值taskAffinity的任務存在;如果存在這樣的任務,它就會在這個任務中啟動,否則就會在新任務中啟動。因此,如果我們想要設定了”singleTask”啟動模式的Activity在新的任務中啟動,就要為它設定一個獨立的taskAffinity屬性值。如果設定了”singleTask”啟動模式的Activity不是在新的任務中啟動時,它會在已有的任務中查看是否已經存在相應的Activity執行個體,如果存在,就會把位於這個Activity執行個體上面的Activity全部結束掉,即最終這個Activity執行個體會位於任務的堆棧頂端中。
- singleInstance,和singleTask相比,不同點在於singleInstance activity所在的task只會有這一個activity
- 返回導航:singleTask和singleInstance啟動的activity,儘管可能不在同一個task,但是仍然會回到原來的activity;但是singleTask可能會存在back stack“拼接”的情況
launchMode驗證測試
主要測試的是singleTask這一屬性,其他的文檔描述都比較清楚,也不容易產生誤解。兩組實驗,不設定taskAffinity和設定taskAffinity時,singleTask的行為。對比兩者“最近任務”,dumpsys,logcat,返回導航的差異。
測試1
不設定taskAffinity,MainActivity -> SingleTaskFirstActivity -> SimpleActivity -> SingleTaskSecondActivity -> 選擇圖片Intent,再一路返回。
最近任務內只能看到一個TaskDemo的任務,dumpsys的結果如下:
所有的activity均在同一個task內,驗證了老羅的結論。另外注意標紅的兩處,它們的ProcessRecord對象不同,ImageGallery和SingleTaskActivity處於不同的進程,也驗證了前文所述“可以運行在不同的進程”。
logcat日誌如下:
一路返回依次回到SingleTaskSecondActivity, SimpleActivity, SingleTaskFirstActivity,MainActivity, 案頭。
測試2
設定SingleTaskFirstActivity的taskAffinity為單獨的值,MainActivity -> SingleTaskFirstActivity。
通過最近任務可以看到,有了兩個TaskDemo的任務。而dumpsys結果如下:
可以看到,MainActivity和SingleTaskFirstActivity運行在了兩個不同的task裡面(棧底為MainActivity的t225, 棧底為SingleTaskFirstActivity的t226),但是他們仍屬於同一進程。
我們接著啟動SimpleActivity,再通過最近任務切換回t225,並且也啟動SimpleActivity,這時通過dumpsys結果如下:
SimpleActivity同時存在兩個執行個體,他們的hashCode是不同的,目前處於resumed狀態的是在t225中的執行個體。
而通過查看logcat:
我們同樣發現SimpleActivity被建立了兩個執行個體。
此時我們通過最近任務切換回t226,並在此啟動SingleTaskSecondActivity,查看dumpsys,然後再切換回t225也啟動SingleTaskSecondActivity,查看dumpsys和logcat:
我們可以看到儘管第一次啟動SingleTaskSecondActivity是在t226,但是SingleTaskSecondActivity執行個體卻運行在t225中,為什麼呢?因為SingleTaskSecondActivity並沒有設定taskAffinity屬性,所以它和MainActivity將會運行在同一個task中!和老羅的分析一致。而第二次啟動時,並未建立新的SingleTaskSecondActivity執行個體,而是調用了它的onNewIntent方法,和文檔描述一致。
現在我們測試一下返回導航,依次按下返回鍵,觀察每次回到的activity,以及每次dumpsys的結果,以及logcat的結果:
第一次回到了t225的SimpleActivity,dumpsys:
第二次回到了t225的MainActivity,dumpsys:
第三次回到了t226的SimpleActivity,dumpsys:
第四次回到了t226的SingleTaskFirstActivity,dumpsys:
最後一次返回就回到了案頭。
logcat日誌如下:
從上面的返迴路徑來看,確實驗證了developer文檔上關於back stack“拼接”的情況的描述,即:並不是按照most recent first的順序返回的,而是優先把一個task的back stack(t225) pop完畢之後,再pop下一個task的back stack(t226)。
測試3
基於測試2的版本,MainActivity -> SingleTaskFirstActivity -> SimpleActivity,再切換回MainActivity,再啟動SingleTaskFirstActivity。
查看最近任務,仍然有兩個task,但是查看dumpsys以及logcat:
我們可以看到,第一次啟動的SimpleActivity就被pop(destroy)了,從而把未在棧頂的SingleTaskFirstActivity提到了棧頂。這也驗證了back stack內的activity不會重新排序,最會pop和push的事實。
Intent flag
只要搞清楚了launchMode中容易產生誤解的地方,對於intent flag來說,只需要記住一點即可:intent flag可以覆蓋launchMode的設定。為了避免內容過於冗長,本文就不對intent flag進行詳細的測試分析了。
總結
- 同一task內的activity可以是來自不同進程的activity
- 棧內的activity不會重新排序,只能push或者pop
- standard模式允許多執行個體,可以在不同的task
- singleTask的activity只會存在一個執行個體
- singleTask的activity如果設定了獨立的taskAffinity屬性值,啟動時就會在新的task中,否則會在已有task中
- singleTask的activity啟動時,它會在目標task(新的task或者已有task)中查看是否已經存在相應的activity執行個體,如果存在,就會把位於這個activity執行個體上面的activity全部銷毀(pop, destroy)掉,即最終這個activity執行個體會位於任務的堆棧頂端中
Android-啟動模式task-lunchmodle-intent flag 總結