Activity啟動模式 Tasks和Back Stack
Task是用戶在進行某項工作時需要與之交互的一系列activities的集合。這些activities按照它們被打開的順序,被安放在一個堆棧里(back stack)。
一個activity甚至可以打開其他應用的activity。
比如你的應用需要發送一個電子郵件,你可以定義一個intent來執行發送動作,intent包含一些必要的數據,然后啟動另一個應用中的activity來執行發送郵件的動作,當郵件發送完,又回到你的activity。
Android會將這兩個activity放在同一個task中來保持一致的用戶體驗。
Activity壓棧
當前的activity啟動一個新的activity時,新的activity會被壓在back stack的棧頂並且獲得焦點。
前一個activity仍然在棧中,但是stopped。
當一個activity停止時,系統會保持它的用戶交互狀態。
當用戶按下Back鍵,當前activity會出棧(這個activity會被銷毀),前一個activity會被恢復,並且它的UI狀態也會被恢復。
當棧中的activities依次彈出,棧空了之后,task就不存在了。
Multitasking:Task在后台
Task也只是一個有凝聚力的單元,當用戶開啟一個新的task,或者Home鍵回到桌面時,task將會變為在后台運行。
當Task在后台時,其中所有的activity都是停止的,back stack保持不變,這個task只是失去了焦點。
Task可以回到前台狀態,這樣用戶就可以回到他們離開的現場,即最頂端的activity會resume。
后台中可以保持多個task,但是系統可能會kill后台的activity用來恢復內存,這樣就會導致activity的狀態丟失。
保存Activity狀態
當activity停止時(stopped),系統的默認行為是保存它的狀態。
然而,你可以,並且應該使用回調方法主動保留你的狀態,以防activity被系統kill而需要重建。
當系統為了恢復內存而kill掉activity時,activity的狀態信息會丟失。
但是系統仍然知道這個activity在back stack中的位置,當這個activity返回到棧頂時,系統會重建(recreate)它,而不是像之前一樣恢復它(resume)。
所以為了不丟失用戶的工作,你需要實現 onSaveInstanceState()
方法來主動地保存數據。
管理Task
Android系統是這樣管理task和back stack的:將activity按照啟動的順序壓入堆棧中,如果一個activity被啟動多次,會創建它的多個實例,然后將新的實例壓入棧中。
如果你想介入並更改這種默認的行為也可以。
比如你可能想在你的某一個activity啟動時新開啟一個task(而不是放在當前task中);
又或者,當你啟動一個activity時,你希望把它的現有實例提到前面來(而不是在棧頂創建一個新的實例);
或者,你希望在用戶離開你的task時清除除了根activity之外的所有activity。
要做這些事,你可以通過manifest中 <activity>
元素的屬性,還可以通過設置 startActivity()
中傳遞的intent的flag來完成。
你可以使用的 <activity>
的屬性:
- taskAffinity
- launchMode
- allowTaskReparenting
- clearTaskOnLaunch
- alwaysRetainTaskState
- finishOnTaskLaunch
以及主要的intent的flag:
定義啟動模式
啟動模式允許你定義一個新的activity實例是如何和當前task關聯的。
你可以通過兩種方式來定義啟動模式:在manifest中定義或者通過用intent的flag。
這里有兩點需要注意:
1.有些啟動模式只能在manifest中定義,也有一些只能通過intent的flag定義。
2.當一個activity A啟動一個activity B時,如果B在它的manifest中定義了啟動模式,但是A通過intent要求它用另一種啟動模式,以A的要求為主。
使用manifest file定義啟動模式
可以在manifest文件中 <activity>
標簽下使用 launchMode
屬性定義activity和task關聯的方式,它規定了activity如何啟動並進入一個task。
注意,在manifest中定義的啟動模式可以被啟動activity的intent中的flag標明的模式覆蓋。
有下面幾種屬性值:
"standard" (the default mode)
默認模式。在當前task中,activity的新實例被創建,並且傳遞intent給它。一個activity可以被實例化多次,每個實例可以屬於不同的task,一個task也可以有多個它的實例。
"singleTop"
如果當前task的頂部已經有一個這個activity的實例,系統就通過 onNewIntent()
方法向這個實例傳遞intent,而不是重新建立activity的實例。
一個activity可以被實例化多次,並且實例可以屬於不同的task,一個task也可以有多個它的實例(頂部的activity不是這個activity的實例時,就會重新實例化)。
"singleTask"
系統將會重新創建一個task並且實例化activity,將其放在task的根部。
然而,如果這個activity的實例已經在一個單獨的task中存在,系統將會調用 onNewIntent()
方法將新的intent傳遞給這個已經存在的實例,而不是重新創建實例。
在任一時刻,只能有一個這個activity的實例存在。
"singleInstance"
和"singleTask"類似,唯一不同的是系統不會在這個activity的實例所在的task中啟動任何其他activity。
這個activity的實例永遠是這個task中的唯一一個成員,這個activity啟動的任何其他activity都將在另外的task中打開。
返回處理
不管activity是在一個新的task啟動還是當前task啟動,返回鍵永遠把用戶帶到之前的那個activity。
但是有一種特殊情況:如果你啟動一個啟動模式為singleTask的activity,如果這個activity在一個后台task存在實例,那么這整個task將會被放到前台,這時候,back stack就會包含這個task中所有的activities,並且它們是放在棧頂。
如下圖:
使用Intent的flag標明啟動模式
通過設置傳遞給 startActivity()
的intent的flag,可以修改要啟動的activity和它的task的關聯模式。
可以使用的flags有:
和之前討論過的"singleTask"相同,在新的task中啟動activity,如果一個你需要的activity的task已經存在,則將它推向前台,恢復其上一個狀態,它通過onNewIntent()收到這個新的intent。
和"singleTop"行為相同,如果被啟動的activity是當前頂部的activity,則已經存在的實例收到 onNewIntent()
,而不是新建實例。
如果被啟動的activity已經在當前task運行,不創建它的新實例,而是銷毀在它之上的其他所有activities,然后通過 onNewIntent()
傳遞一個新的intent給這個恢復了的activity。
這個行為在 launchMode
中沒有對應的屬性值。
注意,如果activity的啟動模式是"standard",它自己也將被移除,然后一個新的實例將被啟動。
這是因為當啟動模式是"standard"時,為了接收新的intent必須創建新的實例。
處理affinities
Affinity指示了activity更傾向於屬於哪個task。
默認情況下,同一個應用的activities傾向於在同一個task中。你可以通過<activity>標簽中的 taskAffinity
來修改這種行為。
詳細內容請查看:API Guides: Tasks and Back Stack
http://developer.android.com/guide/components/tasks-and-back-stack.html
清理Back stack
如果用戶離開一個task很久,系統就會清理這個task中的所有activities,除了根activity。當用戶返回到這個task,只有根activity會被恢復。
有一些activity的屬性,你可以用來改變這一行為:
如果這個屬性在task的根activity中被設置為true,那么上面描述的默認行為不會發生,即便過了很長時間,task仍將會保持所有的activities。
如果這個屬性在task的根activity中被設置為true,每次用戶離開這個task,整個task都會被清到只剩根activity。
這樣用戶永遠只能返回到它最初的狀態,即便離開的時間很短。
這個屬性和上一個很像,但是它作用於單個activity,而不是整個task。
它可以引起任何activity離開,包括根activity。
當它被設置為true時,這個activity只在當前會話中屬於這個task,如果用戶離開再返回,它不會再出現。
開啟一個task
你可以通過給activity一個intent filter(action是"android.intent.action.MAIN",category是"android.intent.category.LAUNCHER"),讓這個activity是一個task的進入點。
如下:
<activity ... > <intent-filter ... > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> ... </activity>
一個這樣的intent filter會使得這個activity的icon和label顯示在程序啟動處,提供了一種方法,使得用戶可以啟動這個activity,當它啟動后,用戶也可以通過它來返回到這個task。
第二個能力是很重要的:用戶必須能夠離開一個task,然后通過activity launcher返回到它。
因為這個原因,兩個讓activity永遠實例化一個task的啟動模式:"singleTask" 和"singleInstance",應該僅在activity有一個 ACTION_MAIN 和CATEGORY_LAUNCHER filter的時候用它們。
參考資料
API Guides: Tasks and Back Stack
http://developer.android.com/guide/components/tasks-and-back-stack.html
<activity>標簽:
http://developer.android.com/guide/topics/manifest/activity-element.html
關於啟動模式,還可以參見博文:
http://www.cnblogs.com/fanchangfa/archive/2012/08/25/2657012.html