一藐唠、概述
Activity 作為與用戶交互的一個窗口榜贴,是使用非常頻繁的一個基本組件毛仪。Android系統(tǒng)是通過Activity棧來管理Activity的,而Activity則是通過哦生命周期來進行自己的創(chuàng)建畸裳、活動與銷毀等。所以掌握Activity生命周期很有必要淳地。
二躯畴、金字塔模型
??官方的描述很形象民鼓,Activity 生命周期的每個階段就是金字塔上的一階。
??當(dāng)系統(tǒng)創(chuàng)建新 Activity 實例時蓬抄,每個回調(diào)方法會將 Activity 狀態(tài)向頂端移動一階丰嘉。 金字塔的頂端是 Activity 在前臺運行并且用戶可以與其交互的時間點。
??當(dāng)用戶開始離開 Activity 時嚷缭,系統(tǒng)會調(diào)用其他方法在金字塔中將 Activity 狀態(tài)下移饮亏,從而銷毀 Activity。
??在有些情況下阅爽,Activity 將只在金字塔中部分下移并等待(比如路幸,當(dāng)用戶切換到其他應(yīng)用時),Activity 可從該點開始移回頂端(如果用戶返回到該 Activity)付翁,并在用戶停止的位置繼續(xù)简肴。
這個模型中包含了Activity的六種狀態(tài):
Created:創(chuàng)建完成
Started:可見(不可交互)
Resumed:可見(活動)
Paused:部分可見(后臺)
Stopped:不可見
Destroyed:銷毀
在這六種狀態(tài)當(dāng)中,只有Resumed百侧、Paused砰识、Stopped這幾種狀態(tài)在用戶沒有進一步操作時會保持在該狀態(tài),而其余的佣渴,都會在執(zhí)行完相應(yīng)的回調(diào)函數(shù)后快速跳過辫狼,很容易理解,resumed 狀態(tài)就是在當(dāng)前界面辛润,后面兩個狀態(tài)是進入了另一個界面活動膨处,如果打開一個dialog或者透明主題(dialog主題)的Activity,這個時候砂竖,只會進入paused狀態(tài)真椿,不會進入stoped狀態(tài)。
一般情況下乎澄,您不得使用 onPause() 永久性存儲用戶更改(比如輸入表格的個人信息)瀑粥。 只有在您確定用戶希望自動保存這些更改的情況(比如,草擬電子郵件時)下三圆,才能在 onPause() 中永久性存儲用戶更改狞换。但您應(yīng)避免在 onPause() 期間執(zhí)行 CPU 密集型工作,比如向數(shù)據(jù)庫寫入信息舟肉,因為這會拖慢向下一 Activity 過渡的過程(您應(yīng)改為在 onStop() 間執(zhí)行高負載關(guān)機操作)修噪。另外一點就是,啟動新的Activity路媚,當(dāng)前的Activity必須onpause進入后臺黄琼,才會開始啟動下一個Activity。
三、異常情況下的生命周期
在有些情況下脏款,您的 Activity 會因正常應(yīng)用行為而銷毀围苫,比如當(dāng)用戶按 返回按鈕或您的 Activity 通過調(diào)用 finish()示意自己的銷毀。 如果 Activity 當(dāng)前被停止或長期未使用撤师,或者前臺 Activity 需要更多資源以致系統(tǒng)必須關(guān)閉后臺進程恢復(fù)內(nèi)存剂府,系統(tǒng)也可能會銷毀 Activity。
當(dāng)您的 Activity 因用戶按了返回 或 Activity 自行完成而被銷毀時剃盾,系統(tǒng)的 Activity 實例概念將永久消失腺占,因為行為指示不再需要 Activity。 但是痒谴,如果系統(tǒng)因系統(tǒng)局限性(而非正常應(yīng)用行為)而銷毀 Activity衰伯,盡管 Activity 實際實例已不在,系統(tǒng)會記住其存在积蔚,這樣意鲸,如果用戶導(dǎo)航回實例,系統(tǒng)會使用描述 Activity 被銷毀時狀態(tài)的一組已保存數(shù)據(jù)創(chuàng)建 Activity 的新實例尽爆。 系統(tǒng)用于恢復(fù)先前狀態(tài)的已保存數(shù)據(jù)被稱為“實例狀態(tài)”怎顾,并且是 Bundle 對象中存儲的鍵值對集合。
注意:每次用戶旋轉(zhuǎn)屏幕時教翩,您的 Activity 將被銷毀并重新創(chuàng)建杆勇。 當(dāng)屏幕方向變化時贪壳,系統(tǒng)會銷毀并重新創(chuàng)建前臺 Activity饱亿,因為屏幕配置已更改并且您的 Activity 可能需要加載備用資源(比如布局)。
默認情況下闰靴,系統(tǒng)會使用 Bundle 實例狀態(tài)保存您的 Activity 布局(比如彪笼,輸入到 EditText 對象中的文本值)中有關(guān)每個 View 對象的信息。 這樣蚂且,如果您的 Activity 實例被銷毀并重新創(chuàng)建配猫,布局狀態(tài)便恢復(fù)為其先前的狀態(tài),且您無需代碼杏死。 但是泵肄,您的 Activity 可能具有您要恢復(fù)的更多狀態(tài)信息,比如跟蹤用戶在 Activity 中進度的成員變量淑翼。
注:為了 Android 系統(tǒng)恢復(fù) Activity 中視圖的狀態(tài)腐巢,每個視圖必須具有 android:id 屬性提供的唯一 ID。
要保存有關(guān) Activity 狀態(tài)的其他數(shù)據(jù)玄括,您必須替代 onSaveInstanceState() 回調(diào)方法冯丙。當(dāng)用戶要離開 Activity 并在 Activity 意外銷毀時向其傳遞將保存的 Bundle 對象時,系統(tǒng)會調(diào)用此方法遭京。 如果系統(tǒng)必須稍后重新創(chuàng)建 Activity 實例胃惜,它會將相同的 Bundle 對象同時傳遞給 onRestoreInstanceState() 和 onCreate() 方法泞莉。
如果Activity A 啟動 B 在啟動 C,如果A和B被回收了船殉,這個時候C返回鲫趁,B會重繪(實例被回收了,但是棧還是在的)
四捺弦、由重建引發(fā)的窗體泄露
Android的每一個Activity都有個WindowManager窗體管理器饮寞,構(gòu)建在某個Activity之上的對話框、PopupWindow也有相應(yīng)的WindowManager窗體管理器列吼。因為Dialog幽崩、PopupWindown不能脫離Activity而單獨存在著,所以當(dāng)承載某個Dialog或者某個PopupWindow正在顯示的Activity被finish()后寞钥,而Dialog(或PopupWindow)沒有正常退出的話慌申,就會拋Window Leaked錯誤了,因為這個Dialog(或PopupWindow)的WindowManager已經(jīng)沒有誰可以附屬了理郑,所以它的窗體管理器就泄漏了蹄溉。
在進入新的Activity時突然轉(zhuǎn)屏(哥們開發(fā)的sdk支持橫豎屏切換),因為在AndroidManifest.xml中沒有配置android:configChanges屬性您炉,此時Activity會重新調(diào)用onCreate方法柒爵,即會重新調(diào)用整個生命周期,而此時的Dialog已經(jīng)顯示并沒有dismiss赚爵,所以造成了窗體泄漏棉胀。解決的方法就變得如此簡單,在AndroidManifest.xml中配置android:configChanges屬性冀膝,這樣當(dāng)我們橫豎屏切換的時候會調(diào)用Activity的onConfigurationChanged方法唁奢,不會重新調(diào)用整個生命周期了。
android:configChanges的一些屬性
1窝剖、不設(shè)置Activity的android:configChanges時麻掸,切屏?xí)匦抡{(diào)用整個生命周期,切橫屏?xí)r會執(zhí)行一次赐纱,切豎屏?xí)r會執(zhí)行兩次
2脊奋、設(shè)置Activity的android:configChanges="orientation"時,切屏還是會重新調(diào)用整個生命周期疙描,切橫诚隙、豎屏?xí)r只會執(zhí)行一次
3、設(shè)置Activity的android:configChanges="orientation|screenSize"一起設(shè)置的時候才是moshi有效的(原因看下面的表格)淫痰。雖然不會重建Activity最楷,但是會回調(diào)Activity里面的一個方法: onConfigurationChanged(Configuration config)
在這里你可以監(jiān)聽了,Activity的什么改變了,比如方向籽孙,比如彈出了鍵盤還是隱藏了moshi鍵盤(清單文件的Activity 添加android:configChanges="keyboard|keyboardHidden“)烈评,如果有需要監(jiān)控其他屬性的需求,請參考底下的表格進行屬性添加
附上開發(fā)藝術(shù)探索的一張圖(侵刪)android:configChanges屬性解釋:
五犯建、Activity的啟動模式
任務(wù)棧
我們知道系統(tǒng)使用棧來管理Activity讲冠,而棧根據(jù)是否在前臺,可以劃分為前臺棧和后臺棧(實際沒有區(qū)別适瓦,根據(jù)當(dāng)前的Activity劃分竿开,即前臺只有一個,后臺可能有多個)玻熙。
- Android任務(wù)棧又稱為Task否彩,它是一個棧結(jié)構(gòu),具有后進先出的特性嗦随,用于存放我們的Activity組件列荔。
- 我們每次打開一個新的Activity或者退出當(dāng)前Activity都會在一個稱為任務(wù)棧的結(jié)構(gòu)中添加或者減少一個Activity組件,因此一個任務(wù)棧包含了一個activity的集合, android系統(tǒng)可以通過Task有序地管理每個activity枚尼,并決定哪個Activity與用戶進行交互:只有在任務(wù)棧棧頂?shù)腶ctivity才可以跟用戶進行交互贴浙。
- 在我們退出應(yīng)用程序時,必須把所有的任務(wù)棧中所有的activity清除出棧時,任務(wù)棧才會被銷毀署恍。當(dāng)然任務(wù)棧也可以移動到后臺, 并且保留了每一個activity的狀態(tài). 可以有序的給用戶列出它們的任務(wù), 同時也不會丟失Activity的狀態(tài)信息崎溃。
- 需要注意的是,一個App中可能不止一個任務(wù)棧盯质,某些特殊情況下袁串,單獨一個Actvity可以獨享一個任務(wù)棧。還有一點就是一個Task中的Actvity可以來自不同的App唤殴,同一個App的Activity也可能不在一個Task中般婆。
那么系統(tǒng)是怎么劃分Activity是在同一個棧里呢到腥?這個時候就要說下TaskAffinity這個屬性了朵逝。
TaskAffinity屬性
TaskAffinity(任務(wù)相關(guān)性),這個參數(shù)標(biāo)識了一個Activity所需要的任務(wù)棧的名字,默認情況下,所有的Activity所需的任務(wù)棧的名字為應(yīng)用的包名.
可以為每個Activity都單獨指定TaskAffinity屬性,不同的名字代表不同的任務(wù)棧android:taskAffinity="屬性值為字符串"。
TaskAffinity如何生效
- TaskAffinity + singleTask (其實就是把singletask放到和包名不一樣的棧乡范,singletask單獨使用配名,不代表不能在包名這個棧,他只表示一旦創(chuàng)建之后晋辆,只允許一個棧存在一個實例)
- TaskAffinity + allowTaskReparenting(允許了其他應(yīng)用的某個Activity無縫遷移進入我們應(yīng)用的Activity棧,一旦再次打開其他應(yīng)用的時候渠脉,又會遷移回去,具體見下面的圖)
- 其他情況是無效的
可以通過 adb shell dumpsys activity activities 命令查看棧的情況
command + K是terminal的清屏快捷鍵
l 在adb命令中顯示的launchMode代表的數(shù)值
standard : launchMode = 0
singleTop : launchMode=1
singleTask: launchMode= 2
singleInstance: launchMode=3
allowTaskReparenting = true 的遷移行為瓶佳,如下圖(來源于網(wǎng)絡(luò)芋膘,侵刪)
不過有點需要說明的是allowTaskReparenting僅限于singleTop和standard模式,這是因為一個activity的affinity屬性由它的taskAffinity屬性定義(代表棧名),而一個task的affinity由它的root activity定義为朋。所以臂拓,一個task的root activity總是擁有和它所在task相同的affinity。由于以singleTask和singleInstance啟動的activity只能是一個task的root activity习寸,因此allowTaskReparenting僅限于以standard 和singleTop啟動的activity
四種啟動模式
我們應(yīng)用中有多個Activity組件胶惰,之間經(jīng)常會進行跳轉(zhuǎn),也有可能需要在本應(yīng)用中打開其它應(yīng)用的的Activity霞溪。當(dāng)我們返回上一個組件時孵滞,我們更希望復(fù)用這個Activity。
但Android系統(tǒng)的stander模式每次都會為我們創(chuàng)建一個新的Activity并添加到Task中鸯匹。另外坊饶,我們開啟一次頁面,它的數(shù)據(jù)和信息狀態(tài)都會被保留殴蓬,這樣會造成數(shù)據(jù)冗余, 重復(fù)數(shù)據(jù)太多, 最終還可能導(dǎo)致內(nèi)存溢出的問題(OOM)幼东。
為了解決這些問題,android系統(tǒng)提供了一套Activity的啟動模式來修改系統(tǒng)Activity的默認啟動行為科雳。目前啟動模式有四種根蟹,分別是standard,singleTop糟秘,singTask和singleInstance简逮,接下來我們將分別介紹這四種模式。
Standard 模式
??又稱為標(biāo)準(zhǔn)模式尿赚,也是系統(tǒng)的默認模式(可以不指定)散庶,在這樣模式下,每啟動一個Activity都會重新創(chuàng)建一個Activity的新實例凌净,并且將其加入任務(wù)棧中悲龟,而且完全不會去考慮這個實例是否已存在。singleTop 模式
??又稱棧頂復(fù)用模式冰寻,顧名思義须教,在這種模式下,如果有新的Activity已經(jīng)存在任務(wù)棧的棧頂斩芭,那么此Activity就不會被重新創(chuàng)建新實例轻腺,而是復(fù)用已存在任務(wù)棧棧頂?shù)腁ctivity。這里重點是位于棧頂划乖,才會被復(fù)用贬养,如果新的Activity的實例已存在但沒有位于棧頂,那么新的Activity仍然會被重建琴庵。需要注意的是误算,Activity的onNewIntent方法會被調(diào)用仰美,方法原型如下:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
}
通過此方法的參數(shù),我們可以獲取當(dāng)前請求的相關(guān)信息儿礼,此時Activity的onCreate筒占、onStart方法不會被調(diào)用,因為Activity并沒有被重建蜘犁。
??這種模式通常比較適用于接收到消息后顯示的界面翰苫,如qq接收到消息后彈出Activity界面,如果一次來10條消息这橙,總不能一次彈10個Activity奏窑,是吧?再比如新聞客戶端收到了100個推送屈扎,你每次點一下推送他都會進入某個activiy界面(顯singleTask 模式
?? 又稱為棧內(nèi)復(fù)用模式埃唯。這是一種單例模式,與singTop點類似鹰晨,只不過singTop是檢測棧頂元素是否有需要啟動的Activity墨叛,而singTask則是檢測整個棧中是否存在當(dāng)前需要啟動的Activity,如果存在就直接將該Activity置于棧頂模蜡,并將該Activity以上的Activity都從任務(wù)棧中移出銷毀漠趁,同時也會回調(diào)onNewIntent方法。示新聞只用一個activity忍疾,只是內(nèi)容不同而已)闯传,這時也比較適合使用singleTop模式。
-
singleTask 模式
?? 又稱為棧內(nèi)復(fù)用模式卤妒。這是一種單例模式甥绿,與singTop點類似,只不過singTop是檢測棧頂元素是否有需要啟動的Activity则披,而singTask則是檢測整個棧中是singleTask 模式否存在當(dāng)前需要啟動的Activity共缕,如果存在就直接將該Activity置于棧頂,并將該Activity以上的Activity都從任務(wù)棧中移出銷毀士复,同時也會回調(diào)onNewIntent方法图谷。
singleTask 模式比較適合應(yīng)用的主界面activity(頻繁使用的主架構(gòu)),可以用于主架構(gòu)的activity判没,(如新聞蜓萄,側(cè)滑隅茎,應(yīng)用主界面等)里面有好多fragment澄峰,一般不會被銷毀,它可以跳轉(zhuǎn)其它的activity 界面再回主架構(gòu)界面辟犀,此時其他Activity就銷毀了俏竞。
-
singleInstance 模式
??在singleInstance模式下绸硕,該Activity在整個android系統(tǒng)內(nèi)存中有且只有一個實例,而且該實例單獨尊享一個Task魂毁。換句話說玻佩,A應(yīng)用需要啟動的MainActivity 是singleInstance模式,當(dāng)A啟動后席楚,系統(tǒng)會為它創(chuàng)建一個新的任務(wù)棧咬崔,然后A單獨在這個新的任務(wù)棧中,如果此時B應(yīng)用也要激活MainActivity烦秩,由于棧內(nèi)復(fù)用的特性垮斯,則不會重新創(chuàng)建,而是兩個應(yīng)用共享一個Activity的實例只祠。
Activity啟動模式的使用方式
如何給Activity指定啟動模式呢兜蠕?事實上共有如下兩種方式:
1. 通過AndroidMenifest.xml文件為Activity指定啟動模式,代碼如下:
<activity android:name=".ActivityC"
android:launchMode="singleTask" />
2. 通過在Intent中設(shè)置標(biāo)志位(addFlags方法)來為Activity指定啟動模式抛寝,示例代碼如下:
Intent intent = new Intent();
intent.setClass(ActivityB.this,ActivityA.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
那么什么是標(biāo)志位呢熊杨?常用的標(biāo)志位有哪一些?
啟動標(biāo)記 Intent Flag
Intent.FLAG_ACTIVITY_NEW_TASK
該標(biāo)志位表示使用一個新的Task來啟動一個Activity盗舰,相當(dāng)于在清單文件中給Activity指定“singleTask”啟動模式晶府。Intent.FLAG_ACTIVITY_SINGLE_TOP
??該標(biāo)志位表示使用singleTop模式來啟動一個Activity,與在清單文件指定android:launchMode="singleTop"效果相同钻趋。Intent.FLAG_ACTIVITY_CLEAR_TOP
該標(biāo)志位表示使用singleTask模式來啟動一個Activity郊霎,與在清單文件指定android:launchMode="singleTask"效果相同。Intent.FLAG_ACTIVITY_NO_HISTORY
??使用該模式來啟動Activity爷绘,當(dāng)該Activity啟動其他Activity后书劝,該Activity就被銷毀了,不會保留在任務(wù)棧中土至。如A-B,B中以這種模式啟動C牵寺,C再啟動D博秫,則任務(wù)棧只有ABD。Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
??使用該標(biāo)識位啟動的Activity不添加到最近應(yīng)用列表,也即我們從最近應(yīng)用里面查看不到我們啟動的這個activity进栽。與屬性android:excludeFromRecents="true"效果相同。
啟動模式中singleTask的特殊情景
特殊情景一
前面我們在分析singleTask模式時翘鸭,提到過singleTask模式有些比較特殊的場景廷粒,現(xiàn)在我們就來了解了解它們。
特殊情景一:現(xiàn)在我們假設(shè)有如下兩個Task棧,分別為前臺任務(wù)棧和后臺任務(wù)棧
從圖中我們看出前臺任務(wù)棧分別為AB兩個Activity烘苹,后臺任務(wù)棧分別為CD兩個任務(wù)棧躲株,而且其啟動模式均為singleTask,此時我們先啟動CD镣衡,然后再啟動AB霜定,再有B啟動D档悠,此時后臺任務(wù)棧便會被切換到前臺,而且這個時候整個后退列表就變成了ABCD望浩,請注意我們這里強調(diào)的是后退列表辖所,而非棧合并。因此當(dāng)用戶點擊back鍵時磨德,列表中的Activity會依次按DCBA順序出棧缘回,如下圖所示:
特殊情景二:
如果上面B不是請求啟動D而是請求啟動C,那么又會是什么情況呢典挑?其實這個時候任務(wù)棧退出列表變成C->B->A切诀,其實原因很簡單,singleTask模式的ActivityC切換到棧頂時會導(dǎo)致在他之上的棧內(nèi)的Activity出棧搔弄。其他情況都一樣幅虑。
特殊場景三:
- StartActivityForResult的時候,requestCode必須>0,否則收不到result
看下以下關(guān)系圖
這是為什么呢顾犹?
這是因為ActivityStackSupervisor類中的startActivityUncheckedLocked方法在5.0中進行了修改倒庵。在5.0之前,當(dāng)啟動一個Activity時炫刷,系統(tǒng)將首先檢查Activity的launchMode擎宝,如果為A頁面設(shè)置為SingleInstance或者B頁面設(shè)置為singleTask或者singleInstance,則會在LaunchFlags中加入FLAG_ACTIVITY_NEW_TASK標(biāo)志,而如果含有FLAG_ACTIVITY_NEW_TASK標(biāo)志的話浑玛,onActivityResult將會立即接收到一個cancle的信息绍申,而5.0之后這個方法做了修改,修改之后即便啟動的頁面設(shè)置launchMode為singleTask或singleInstance顾彰,onActivityResult依舊可以正常工作极阅,也就是說無論設(shè)置哪種啟動方式,StartActivityForResult和onActivityResult()這一組合都是有效的涨享。所以如果你目前正好基于5.0做相關(guān)開發(fā)筋搏,不要忘了向下兼容,這里有個坑請注意避讓厕隧。
TaskAffinity與allowTaskReparenting和singleTask結(jié)合時可能發(fā)生的應(yīng)用場景
TaskAffinity與singleTask應(yīng)用場景
假如現(xiàn)在有這么一個需求,我們的客戶端app正處于后臺運行奔脐,此時我們因為某些需要,讓微信調(diào)用自己客戶端app的某個頁面吁讨,用戶完成相關(guān)操作后髓迎,我們不做任何處理,按下回退或者當(dāng)前Activity.finish()建丧,頁面都會停留在自己的客戶端(此時我們的app回退棧不為空)排龄,這顯然不符合邏輯的,用戶體驗也是相當(dāng)出問題的茶鹃。我們要求是涣雕,回退必須回到微信客戶端,而且要保證不殺死自己的app.這時候我們的處理方案就是艰亮,設(shè)置當(dāng)前被調(diào)起Activity的屬性為:
LaunchMode=""SingleTask" taskAffinity="com.tencent.mm"
其中com.tencent.mm是借助于工具找到的微信包名闭翩,就是把自己的Activity放到微信默認的Task棧里面挣郭,這樣回退時就會遵循“Task只要有Activity一定從本Task剩余Activity回退”的原則,不會回到自己的客戶端疗韵;而且也不會影響自己客戶端本來的Activity和Task邏輯兑障。
TaskAffinity與allowTaskReparenting應(yīng)用場景
一個e-mail應(yīng)用消息包含一個網(wǎng)頁鏈接,點擊這個鏈接將觸發(fā)一個activity來顯示這個頁面蕉汪,雖然這個activity是瀏覽器應(yīng)用定義的流译,但是activity由于e-mail應(yīng)用程序加載的,所以在這個時候該activity也屬于e-mail這個task者疤。如果e-mail應(yīng)用切換到后臺福澡,瀏覽器在下次打開時由于allowTaskReparenting值為true,此時瀏覽器就會顯示該activity而不顯示瀏覽器主界面驹马,同時actvity也將從e-mail的任務(wù)棧遷移到瀏覽器的任務(wù)棧革砸,下次打開e-mail時并不會再顯示該activity
清空任務(wù)棧
Android系統(tǒng)除了給我提供了TaskAffinity來指定任務(wù)棧名稱外,還給我提供了清空任務(wù)棧的方法糯累,在一般情況下我們只需要在<activity>標(biāo)簽中指明相應(yīng)的屬性值即可算利。
如果用戶離開一個task很久,系統(tǒng)就會清理這個task中的所有activities泳姐,除了根activity效拭。當(dāng)用戶返回到這個task,只有根activity會被恢復(fù)胖秒。
有一些activity的屬性缎患,你可以用來改變這一行為:
android:clearTaskOnLaunch
這個屬性用來標(biāo)記是否從task清除除根Activity之外的所有的Activity,“true”表示清除阎肝,“false”表示不清除较锡,默認為“false”。這里有點我們必須要注意的盗痒,這個屬性只對任務(wù)棧內(nèi)的root Activity起作用蚂蕴,任務(wù)棧內(nèi)其他的Activity都會被忽略。如果android:clearTaskOnLaunch屬性為“true”俯邓,每次我們重新android:clearTaskOnLaunch進入這個應(yīng)用時骡楼,我們只會看到根Activity,任務(wù)棧中的其他Activity都會被清除出棧稽鞭。
??比如一個應(yīng)用的Activity A,B,C鸟整,其中A 的clearTaskOnLaunch設(shè)置為true,C為默認值朦蕴,我們依次啟動A,B,C篮条,點擊HOME,再在桌面點擊圖標(biāo)弟头。啟動的是A,而B涉茧,C將都被移除當(dāng)前任務(wù)棧赴恨。也就是說,當(dāng)Activity的屬性clearTaskOnLaunch為true時將被優(yōu)先啟動伴栓,其余的Activity(B伦连、C)都被移除任務(wù)棧并銷毀,除非前面A已經(jīng)finish銷毀钳垮,后面的已注冊clearTaskOnLaunch為true的activity(B)才會生效惑淳。
??特別地,如果我們的應(yīng)用中引用到了其他應(yīng)用的Activity饺窿,這些Activity設(shè)置了android:allowTaskReparenting屬性為“true”歧焦,則它們會被重新宿主到有共同affinity的task中。
android:finishOnTaskLaunch
finishOnTaskLaunch屬性與clearTaskOnLaunch 有些類似肚医,它們的區(qū)別是finishOnTaskLaunch是作用在自己身上(把自己移除任務(wù)棧绢馍,不影響別的Activity),而clearTaskOnLaunch則是作用在別人身上(把別的Activity移除任務(wù)棧)忍宋,如果我們把Activity的android:finishOnTaskLaunch屬性值設(shè)置為true時痕貌,離開這個Activity所依賴的任務(wù)棧后,當(dāng)我們重新返回時糠排,該Activity將會被finish掉舵稠,而且其他Activity不會受到影響。
android:alwaysRetainTaskState
alwaysRetainTaskState實際上是給了當(dāng)前Activity所在的任務(wù)棧一個“免死金牌”入宦,如果當(dāng)前Activity的android:alwaysRetainTaskState設(shè)置為true時哺徊,那么該Activity所在的任務(wù)棧將不會受到任何清理命令的影響,一直保持當(dāng)前任務(wù)棧的狀態(tài)乾闰。
應(yīng)用場景:
- singleTop適合接收通知啟動的內(nèi)容顯示頁面落追。例如,某個新聞客戶端的新聞內(nèi)容頁面涯肩,如果收到10個新聞推送轿钠,每次都打開一個新聞內(nèi)容頁面是很煩人的。聊天的對話窗口病苗,
singleTask適合作為程序入口點疗垛。例如瀏覽器的主界面。不管從多少個應(yīng)用啟動瀏覽器硫朦,只會啟動主界面一次贷腕,其余情況都會走onNewIntent,并且會清空主界面上面的其他頁面。之前打開過的頁面泽裳,打開之前的頁面就ok瞒斩,不再新建。
singleTask:a界面購物涮总,b界面確認訂單胸囱,c界面付款,如果付款成功會跳到a妹卿,如果不付款則返回b旺矾,這時候重啟a就會用到singleTask.singleInstance適合需要與程序分離開的頁面蔑鹦。例如鬧鈴提醒夺克,將鬧鈴提醒與鬧鈴設(shè)置分離。singleInstance不要用于中間頁面嚎朽,如果用于中間頁面铺纽,跳轉(zhuǎn)會有問題,比如:A -> B (singleInstance) -> C哟忍,完全退出后狡门,在此啟動,首先打開的是B锅很。
standard 標(biāo)準(zhǔn)的啟動模式其馏,也是默認的啟動模式。
隱式啟動Activity爆安,intentFilter匹配規(guī)則
這一部分參考的 http://www.reibang.com/p/151640add690
啟動activity分為兩種叛复,顯式啟動和隱式啟動。顯式:明確指出被調(diào)用activity的包名類名扔仓,隱式調(diào)用不需要明確信息褐奥。顯式和隱式原則上是不共存的,如果共存以顯示為主翘簇。隱式啟動匹配信息在AndroidManifest的activity中的<intent-filter>撬码,三種過濾信息:action,category版保,data呜笑。三個信息可同時存在多個。intent-filter也可同時存在多個彻犁,匹配其中一組intent-filter的三種信息各一種即可叫胁。
匹配規(guī)則
action
區(qū)分大小寫,action系統(tǒng)有自定義一些袖裕,action匹配字符串必須一樣曹抬。若intentFilter定義了action屬性,隱式啟動至少匹配其中一個。
category
Intent未指定category時谤民,系統(tǒng)會默認給Intent增加category屬性:<category android:name="android.intent.category.DEFAULT" 堰酿,所以如果你隱式啟動activity且不想指定category在AndroidManifest總定義隱式啟動時,需加上<category android:name="android.intent.category.DEFAULT"张足。
Intent指定category触创,指定一個必須正確匹配一個,多個必須正確匹配多個为牍。
data
intentFilter配置data哼绑,Intent隱式啟動必須匹配至少一個,和action類似
先介紹一種結(jié)構(gòu) URI:
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
例如
content://com.example.test:100/folder/subfolder/test
http://www.baidu.com:80/search/info
data的所有匹配屬性如下:
<data
android:mimeType="string"
android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPrefix="string"
android:pathPattern="string"/>
主要分為兩種碉咆,一種是mimeType,一種是URI中的其中任何之一屬性抖韩。
屬性簡介:
mimeType:媒體類型,image/jpeg,image/png,image/* 疫铜、video/等等
Scheme:URI模式茂浮,http、file壳咕、content等席揽,URI無此參數(shù)URI無效
Host:URI主機名,www.baidu.com等谓厘,URI無此參數(shù)URI無效
Port:URI中端口號
Path/PathPrefix/PathPattern:路徑信息幌羞,path和pathPattern表示完整的路徑信息,pahPatten可包含通配符"",PathPrefix路徑的前綴信息竟稳。
設(shè)置方法三種:
mIntent.setType(mType)
mIntent.setData(mUri)
mIntent.setDataAndType(mUri,mType)
若先setType再setData属桦,mimeType會被清空
若先setData再setType,data會被清空
原因看源碼,setType和seData類似
public Intent setData(Uri data) {
mData = data;
mType = null;
return this;
}
如下兩種屬性同時使用住练,標(biāo)明這是一個入口activity地啰,并且會出現(xiàn)在系統(tǒng)應(yīng)用列表中
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"