做者原創鏈接 共分3篇:html
Android官網介紹Activity的啓動模式時比較含糊,介紹Application,Activity,Task,Process,Thread等概念以及它們之間的關係時,也沒有說得清楚。你們看了Android官網對Activity啓動模式的介紹後,可能會以爲很困惑。官網介紹singleTask啓動模式時,說只要啓動singleTask啓動模式的Activity就會新建Task,但在實際操做中,若是同一個應用的兩個Activity,若是其中一個Activity的啓動模式爲singleTask,一個Activity的啓動模式爲standard,從其中一個Activity跳轉到另一個Activity,並不會新建Task。android
爲了解除這些困惑,對Activity啓動模式作了深刻研究,所以寫了這一系列博客,詳細闡述Application,Activity,Task,Process,Thread等概念概念之間的關係,以及啓動模式各自的特色,但願能對你們理解這些概念有所幫助。shell
咱們知道在AndroidManifest.xml裏可聲明Application,它能夠由4大組件組成:Activity,Service,ContentProvider,BroadcastReceiver。聲明Application時能夠用android:name聲明本應用使用的Application類,若是沒有聲明,則會直接使用Android框架的Application類創建實例對象。app
應用第一次啓動時,會啓動一個新進程,該進程用應用的包名做爲進程名。該進程會啓動主線程ActivityThread,也叫作UI線程,UI的繪製都在該線程裏完成。該進程裏還有一些Binder服務線程,用於和系統進行通訊。框架
另外,咱們知道Activity跳轉時,能夠跨應用跳轉,也就說應用app1裏的Activity A能夠跳轉到應用app2裏的Activity B。若是Activity A和Activity B的啓動模式爲standard模式,從A跳轉到B後,Activity A和Activity B對應的ActivityRecord會放在同一個task裏(ActivityRecord,Task都由系統進程管理,下一篇博客會介紹這些概念),可是Acitivity A和Activity B的實例對象會放在不一樣的進程裏。假設app1的包名爲com.cloud.app1,app2的包名爲com.cloud.app2,那麼Activity A的實例對象位於進程com.cloud.app1裏,Activity B的實例對象位於進程com.cloud.app2裏。ide
也就是說,每一個應用的組件都會運行在對應的應用進程裏,該進程用應用的包名做爲進程名。該進程裏有一個主線程,也叫作UI線程,應用組件都運行在UI線程裏。只有一種狀況例外,若是聲明組件時用android:process設置了進程名,該組件就會運行在一個新進程裏,不是以應用的包名做爲進程名,而是用包名+:+設置的值做爲進程名post
因此通常狀況下service,receiver也會運行在ui線程裏,若是在service,receiver的生命週期方法裏作一些耗時的操做,系統會提示ANR(Activity Not Responde)錯誤。ui
Activity啓動時ActivityManagerService會爲其生成對應的ActivityRecord記錄,並將其加入到回退棧(back stack)中,另外也會將ActivityRecord記錄加入到某個Task中。請記住,ActivityRecord,backstack,Task都是ActivityManagerService的對象,由system_server進程負責維護,而不是由應用進程維護。spa
在回退棧裏屬於同一個task的ActivityRecord會放在一塊兒,也會造成棧的結構,也就是說後啓動的Activity對應的ActivityRecord會放在task的棧頂。線程
假設Activity的跳轉順序:A–>B–>C,A,B,C對應的ActivityRecord屬於同一個Task,此時從C跳轉至D,再跳轉至E,C和D不屬於同一個Task,D和E屬於同一個Task,那如今的back stack結構以下所示:
如今A,B,C屬於task1,C在task1的棧頂,D,E屬於task2,E在task2的棧頂。也能夠看出來task2位於整個回退棧的棧頂,也就是說task2在task1的上面。若是此時不斷按回退鍵,看到的Activity的順序會是E–>D–>C–>B–>A。
另外需注意,ActivityManagerService不只會往回退棧裏添加新的ActivityRecord,還會移動回退棧裏的ActivityRecord,移動時以task爲單位進行移動,而不會移動單個AcitivityRecord。仍是針對上面的例子,假設此時按了Home鍵,那麼會將Home應用程序(也叫作Launcher應用程序)的task移動至棧頂,那麼此時回退棧以下所示:
能夠看到Home應用程序的Activity H對應的Activity Record移動到了回退棧的棧頂。Home應用程序的Activity H對回退按鍵的響應作了特殊處理,若是此時按回退鍵,是看不到Activity E的。
若是此時經過Launcher程序再打開Activity A所在的應用,那麼會顯示Activity C,由於會將Activity A對應的Activity Record所在的task移動至回退棧的棧頂,此時回退棧以下所示:
如今task1移動到了棧頂,Home應用程序的task位於task1的下面,而task2位於Home應用程序的task之下,此時若是按返回鍵,那麼Activity的顯示順序是:C–>B–>A–>H,不會顯示E。
固然咱們也能夠在Launcher應用程序裏打開D所在的應用,這樣會將D,E所在的task2移動至棧頂。
如今應該對task有所理解了,task實際上是由ActivityRecord組成的棧,多個task以棧的形式組成了回退棧,ActivityManagerService移動回退棧裏的ActivityRecord時以task爲單位移動。
咱們知道跨應用跳轉Activity時,兩個Activity對應的ActivityRecord可屬於同一個task,那什麼狀況下兩個ActivityRecord會屬於不一樣的task呢?或者說,Activity跳轉時,什麼狀況下會產生新的task呢?
這個問題和Activity的啓動模式,taskAffinity,啓動Activity時爲Intent設置的flag等因素相關。
先說一下taskAffinity,每一個Activity的taskAffinity屬性值默認爲包名,也就是說若是Activity A所在的應用的包名爲com.cloud.app1,那麼Activity A的taskAffinity屬性值爲com.cloud.app1,咱們能夠在AndroidManifest.xml裏經過android:taskAffinity屬性爲Activity設置特殊的taskAffinity,假設咱們在AndroidManifest.xml裏爲Activity A設置了android:taskAffinity=」:test」,那麼Activity A的taskAffinity值爲com.cloud.app1:test。
那麼我如今能夠明白:不一樣應用的Activity的taskAffinity屬性值會不同。
假設Activity A和Activity B的啓動模式都是standard,兩者taskAffinity屬性值不同,從Activity A跳轉至Activity B,那麼它們對應的ActivityRecord會屬於同一個task。
假設Activity A的啓動模式是standard,Activity B的啓動模式singleTask,兩者taskAffinity屬性值同樣,此時從Activity A跳轉至Activity B,那麼它們對應的ActivityRecord會屬於同一個task。由於只要兩個Activity的taskAffinity屬性一致,即便其中有一個Activity的啓動模式爲singleTask,它們對應的ActivityRecord會放在同一個task裏,不論是從某個Activity跳轉到singleTask類型的Activity,仍是從singleTask類型的Activity跳轉到其餘Activity都是如此,除非跳轉的其餘Activity的啓動模式是singleInstance。這裏的描述和官方文檔很不同,稍後會爲你們介紹singleTask啓動模式的特色。
假設Activity A的啓動模式是standard,Activity B的啓動模式singleTask,兩者taskAffinity屬性值不 同樣,此時從Activity A跳轉至Activity B,那麼它們對應的ActivityRecord會屬於不一樣的Task。
還有其餘不少狀況會產生新的task,你們能夠看接下來關於啓動模式的特色的介紹。
Activity的啓動模式共有4種,默認爲standard,其它還有singleTop,singleTask,singleInstance,下面就這4種啓動模式分別介紹它們的特色。
standard模式的Activity能夠有多個ActivityRecord加入不一樣的task,同一個task也可存在多個ActivityRecord,而且ActivityRecord還可相鄰。
假設Activity A的啓動模式爲standard,那麼可能存在以下圖所示的回退棧:
假設Activity A啓動Activity B,B的啓動模式爲standard模式
B的ActivityRecord默認會放在A的ActivityRecord所在的task裏,即便B和A的taskAffinity不一樣也會如此,這也意味着若是B和A屬於不一樣的應用,B的ActivityRecord也會放在A的ActivityRecord所在的task裏。
可是下面兩種狀況不會將A和B的ActivityRecord放在同一個task裏:
若是Activity A的啓動模式爲singleInstance,則會查找整個回退棧,直到找到和B相關的task,而後把B的ActivityRecord放到該task裏,若是沒有找到相關的task,則新建task,將B的ActivityRecord放到新task裏。後面會介紹如何判斷Activity和某個task相關。
若是Activity A的啓動模式爲singleTask,而且Activity A和Activity B的taskAffinity不同,那麼也會查找整個回退棧,直到找到和B相關的task,而後把B的ActivityRecord放到該task裏。
singleTop模式與standard模式比較類似,singleTop模式的Activity能夠有多個ActivityRecord加入不一樣的task,同一個task也可存在多個ActivityRecord,可是同一個task的ActivityRecord不能夠相鄰。
假設Activity A的啓動模式爲singleTop,那麼以下圖所示的回退棧就是不合理的:
可是可存在以下圖所示的回退棧:
假設Activity A啓動了Activity B, 這時B在task的棧頂,B的啓動模式爲singleTop模式。此時從其它Activity也跳轉至Activity B,而且啓動的task也是已啓動的A和B所在的task,或者A和 B所在的task自己就回退棧的棧頂,那麼不會新建B的ActivityRecord,而是會將啓動Activity B的Intent傳遞給棧頂Activity B的ActivityRecrod對應的在應用進程的實例對象,調用它的onNewIntent方法。
能夠這樣模擬此種狀況:
假設Activity A和Activity B在同一個應用app1裏,A是入口Activity,A可跳轉至Activity B,B的啓動模式爲singleTop。此時已從A跳轉至B,通知欄有一個啓動B的通知,點擊通知後,就出現上述狀況。
singleTask模式和standard模式,singleTop模式區別很大,singleTask模式的Activity在整個回退棧只能夠有一個ActivityRecord,也就是說它只能屬於某一個task,不可在多個task裏存在ActivityRecord。可是在這個task裏能夠有其它Activity的ActivityRecord。
假設Activity A的啓動模式爲singleTask,那麼以下圖所示的回退棧就是不合理的:
假設Activity A的啓動模式爲singleTask,那麼以下圖所示的回退棧就是合理的:
假設Activity A的啓動模式爲singleTask,那麼和Activity A的ActivityRecord放在同一個task裏的ActivityRecord所對應的Activity,必須與Activity A的taskAffinity相同。也就是說,Activity A的ActivityRecord只會和同一應用的其它Activity的ActivityRecord放在同一個task裏,而且這些同一應用的其它Activity不能設置特殊的taskAffinity。
singleTask模式的Activity還有另外一個特色:
假設Activity A的啓動模式是singleTask,A所在的task裏,A並無處於棧頂,此時若從別的Activity跳轉至Activity A,那麼A所在的task裏位於A之上的全部ActivityRecord都會被清除掉。
跳轉以前回退棧的示意圖以下所示:
此時從E跳轉至A以後,回退棧的示意圖以下圖所示:
也就是說位A所在的task裏的C被清除了。
另外需注意:
只要兩個Activity的taskAffinity屬性一致,即便其中有一個Activity的啓動模式爲singleTask,它們對應的ActivityRecord會放在同一個task裏,不論是從某個Activity跳轉到singleTask類型的Activity,仍是從singleTask類型的Activity跳轉到其餘Activity都是如此,除非跳轉的其餘Activity的啓動模式是singleInstance。Android官方文檔對singleTask啓動模式的描述不許確。
舉例以下:
假設某個應用有兩個Activity A和Activity B,Activity A已啓動,Activity B的啓動模式爲singleTask,Activity B還從未啓動過,在AndroidManifest.xml裏沒有給這兩個Activity設置特殊的taskAffinity。此時從Activity A跳轉至Activity B,那麼Activity B的ActivityRecord會放在Activity A的ActivityRecord所在的task裏。
該啓動模式和singleTask相似,singleInstance模式的Activity在整個回退棧只能夠有一個ActivityRecord,也就是說它只能屬於某一個task,不可在多個task裏存在ActivityRecord,而且它所在的task不可再有其它Activity的ActivityRecord,即便是同一個應用內的其它Activity,也不可有它們的AcvitityRecord。
假設Activity A的啓動模式爲singleInstance,那麼以下圖所示的回退棧就是不合理的:
假設Activity A的啓動模式爲singleInstance,那麼以下圖所示的回退棧就是合理的:
啓動Activity時,有時須要查看回退棧,看是否有和這個Activity相關的task。Activity和某個task相關,有兩種狀況(假設Activity爲A,相關的task爲task1):
相信你們看了這3篇博客之後,能夠回答以下關於哪些狀況下會產生新task的問題了