首先說(shuō)一下為何activity需要啟動(dòng)模式。
記得有一次一位同事問(wèn)過(guò)我一個(gè)問(wèn)題,他從項(xiàng)目首頁(yè)A跳轉(zhuǎn)選地址頁(yè)面B,選好地址回到首頁(yè)面A,一切正常,雙擊back退出程序,發(fā)現(xiàn)還在首頁(yè)面A。
由于我處理這種問(wèn)題的時(shí)候,一般是B頁(yè)面直接調(diào)用finish方法,結(jié)果解決問(wèn)題繞了很大的彎路,最后發(fā)現(xiàn)他是在B頁(yè)面調(diào)用startactivity方法回到A,這也是最根本問(wèn)題所在。
在默認(rèn)的情況下,當(dāng)我們多次啟動(dòng)一個(gè)activity的時(shí)候,系統(tǒng)就會(huì)創(chuàng)建多個(gè)實(shí)例并把他們一一放入任務(wù)棧中,當(dāng)我們按back鍵或者調(diào)用finish方法的時(shí)候,會(huì)發(fā)現(xiàn)這些activity一一回退。任務(wù)棧是一種“后進(jìn)先出”的棧結(jié)構(gòu),這比較好理解,每次按一下back鍵或調(diào)用finish方法,就會(huì)有一個(gè)activity出棧,直到??諡橹?,當(dāng)棧中無(wú)任務(wù)activity的時(shí)候,系統(tǒng)就會(huì)回收這個(gè)任務(wù)棧。
這就有問(wèn)題了,多次啟動(dòng)同一個(gè)activity,系統(tǒng)就會(huì)重復(fù)創(chuàng)建多個(gè)實(shí)例,能不能就創(chuàng)建一次呢?這就涉及到了android的啟動(dòng)模式(LaunchMode)
目前activity有四種啟動(dòng)模式:standard(標(biāo)準(zhǔn)模式)、singleTop(棧頂復(fù)用模式)、singleTask(棧內(nèi)復(fù)用模式)、singleInstance(單實(shí)例模式)。
- standard:標(biāo)準(zhǔn)模式,也是系統(tǒng)默認(rèn)的模式。每次啟動(dòng)一個(gè)activity,都會(huì)重新創(chuàng)建一個(gè)新的實(shí)例,不管這個(gè)實(shí)例是否已經(jīng)存在。在這種模式下,誰(shuí)啟動(dòng)了這個(gè)activity,這個(gè)activity就運(yùn)行在啟動(dòng)他的哪個(gè)activity所在的棧中。
- singleTop:棧頂復(fù)用模式,在這種模式下,如果新activity已經(jīng)位于任務(wù)棧的棧頂,那么此activity不會(huì)被重新創(chuàng)建。也不涉及生命周期的變化,同時(shí)他的onNewIntent方法會(huì)被調(diào)用,通過(guò)此方法的參數(shù)我們可以取出當(dāng)前的請(qǐng)求信息。但如果新的activity實(shí)例已經(jīng)存在但并不在棧頂,那么activity仍然會(huì)重建。
- singleTask:棧內(nèi)復(fù)用模式,這是一種單實(shí)例模式,在這種模式下,activity只要在棧中存在(參照屬性TaskAffinity),那么多次啟動(dòng)此activity都不會(huì)重新創(chuàng)建實(shí)例,系統(tǒng)也會(huì)回調(diào)其onNewIntent。當(dāng)跳轉(zhuǎn)該activity的時(shí)候,默認(rèn)具有clearTop效果,該activity棧頂所有activity都會(huì)銷毀。如果不存在,那么根據(jù)TaskAffinity查找該任務(wù)棧,如果任務(wù)棧不存在,先創(chuàng)建任務(wù)棧再將activity放入棧中。
- singleInstance:?jiǎn)螌?shí)例模式。這是一種加強(qiáng)的singleTask模式,他和singleTask的區(qū)別就是,此模式的activity只能單獨(dú)的位于一個(gè)任務(wù)棧。

【說(shuō)明:此問(wèn)題我有所懷疑,所以進(jìn)行測(cè)試,如上圖三個(gè)activity,測(cè)試流程activity1 -> activity2 -> activity3 -> activity4 -> back鍵,發(fā)現(xiàn)按完back鍵之后,回到acvitity1,此測(cè)試說(shuō)明從singleInstance再次啟動(dòng)的activity,放入了默認(rèn)任務(wù)?!?br> 那么,標(biāo)準(zhǔn)模式的TaskAffinity是否生效呢?繼續(xù)測(cè)試:

【說(shuō)明:同上面一樣的測(cè)試,測(cè)試流程activity1 -> activity2 -> activity3 -> activity4 -> back鍵,發(fā)現(xiàn)按完back鍵之后,回到acvitity2,此測(cè)試說(shuō)明從singleInstance再次啟動(dòng)的activity,放在哪個(gè)任務(wù)棧,是由TaskAffinity決定的】
那么什么是TaskAffinity呢?
TaskAffinity,也就是activity所需的任務(wù)棧,翻譯為任務(wù)相關(guān)性。這個(gè)參數(shù)標(biāo)識(shí)了一個(gè)activity所需要的任務(wù)棧的名字,默認(rèn)情況下,所有activity所需的任務(wù)棧的名字為應(yīng)用包名。我們可以為每個(gè)activity單獨(dú)指定TaskAffinity屬性,也如上面途中所示。
說(shuō)到TaskAffinity,不得不提一下allowTaskReparenting屬性
allowTaskReparenting一般和TaskAffinity配對(duì)使用,當(dāng)一個(gè)應(yīng)用A啟動(dòng)了應(yīng)用B的某個(gè)Activity后,如果這個(gè)activity的allowTaskReparenting屬性為true,那么當(dāng)應(yīng)用B被啟動(dòng)后,此activity會(huì)直接從應(yīng)用A的任務(wù)棧轉(zhuǎn)移到應(yīng)用B的任務(wù)棧中。
提示:參照standard介紹,如不設(shè)置該屬性,activity應(yīng)該在A棧中,所以默認(rèn)情況下如果應(yīng)用B棧中本就有任務(wù),這時(shí)候按back鍵應(yīng)該還在B應(yīng)用中而非A應(yīng)用中。
上面圖片所示的為AndroidMenifest中的啟動(dòng)方法,還有一種Intent中設(shè)置標(biāo)志位來(lái)為activity指定啟動(dòng)模式
Intent intent = new Intent(this, GuideDetailsActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
這兩種方式都可以為activity指定啟動(dòng)模式,區(qū)別1:優(yōu)先級(jí)上,第二種方式的優(yōu)先級(jí)要高于圖片中所示,當(dāng)兩種同事存在時(shí),以第二種方式為準(zhǔn)。區(qū)別2:上述兩種方式在限定范圍上有所不同,比如第一種方式無(wú)法直接為activity設(shè)定FLAG_ACTIVITY_CLEAR_TOP標(biāo)識(shí),而第二種方式無(wú)法為activity指定singleInstance
Activity的Flags
activity的Flags有很多,這里列舉一些比較常見的標(biāo)記位。大部分情況下,我們不需要為activity指定標(biāo)記位,這里只簡(jiǎn)單介紹下。
FLAG_ACTIVITY_NEW_TASK 這個(gè)標(biāo)記位的作用是指定singleTask啟動(dòng)模式
FLAG_ACTIVITY_SINGLE_TOP 這個(gè)標(biāo)記位的作用是指定singleTop啟動(dòng)模式
FLAG_ACTIVITY_CLEAR_TOP 具有此標(biāo)記的activity,當(dāng)他啟動(dòng)時(shí),在同一個(gè)任務(wù)棧中所有位于他上面的activity都要出棧。這個(gè)標(biāo)記一般和singleTask啟動(dòng)模式一起出現(xiàn),在這種模式下,如果實(shí)例已經(jīng)存在,那么系統(tǒng)就會(huì)調(diào)用他的onNewIntent。如果被啟動(dòng)的activity采用standard模式啟動(dòng),那么他連同他之上的activity都要出棧,系統(tǒng)會(huì)創(chuàng)建新的activity實(shí)例并放入棧頂。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 相當(dāng)于activity的android:excludeFromRecents="true"屬性,表示該activity不會(huì)出現(xiàn)在歷史activity中,對(duì)于這個(gè)我進(jìn)行了一個(gè)測(cè)試:

模擬器測(cè)試該屬性不生效,本人真機(jī)測(cè)試用的是樂(lè)視手機(jī)樂(lè)1s,只有按圖中,在初始activity中設(shè)置才生效,其他activity中無(wú)效果。如果有其他情況,歡迎評(píng)論中留言,感激不盡!
Activity的IntentFilter匹配規(guī)則
啟動(dòng)activity分為兩種方法,顯式調(diào)用和隱式調(diào)用,顯示調(diào)用就是startactivity方式,應(yīng)該都比較熟悉,隱式調(diào)用就是IntentFliter形式,IntentFliter為過(guò)濾屬性,限制匹配到合適的activity,二者共存的時(shí)候,以顯式為主。
IntentFliter中的過(guò)濾信息有action(行動(dòng))、category(類別)、data(數(shù)據(jù)),下面是一個(gè)過(guò)濾規(guī)則的示例:

只有一個(gè)intent同時(shí)匹配action類別,category類別,data類別才算完全匹配,只有完全匹配才能成功啟動(dòng)目標(biāo)activity。
下面詳細(xì)分析各種屬性的匹配規(guī)則:
- action的匹配規(guī)則:action是一個(gè)字符串,系統(tǒng)預(yù)定義了一些action,同時(shí)我們也可以在應(yīng)用中定義自己的action,action的匹配規(guī)則是intent中的action必須能夠和過(guò)濾規(guī)則中的action字符串完全一樣,多個(gè)action只要能過(guò)濾規(guī)則中的任何一個(gè)action相同即可匹配成功,intent中如果沒有指定action,擇匹配失敗。
- category匹配規(guī)則:category是一個(gè)字符串,系統(tǒng)預(yù)定義了一些category,同時(shí)我們也可以在應(yīng)用中定義自己的category,要求intent可以沒有category,但是如果你一旦有category,不管有幾個(gè),每個(gè)都要能夠和過(guò)濾規(guī)則中的任何一個(gè)category相同。(如果沒設(shè)置,默認(rèn)android.intent.category.DEFAULT)
- data的匹配規(guī)則:data和action類似,如果規(guī)則中定義了data,那么intent中必須也要定義可匹配的data。
data的語(yǔ)法定義:
<data android:mimeType="string"
android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"></data>
data由兩部分組成,mimeType和URI,mimeType指媒體類型,比如:image/jpeg、video/等,可以標(biāo)識(shí)圖片、文本、視頻等不同的媒體格式(此時(shí)如不指定,默認(rèn)過(guò)濾規(guī)則為content和file),而URI包含的數(shù)據(jù)就比較多了,下面是URI的結(jié)構(gòu):
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
比如:
content://come.exmple.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info
下面介紹每個(gè)數(shù)據(jù)的含義:
Scheme:URI的模式,比如http、file、content等,如果URI中沒有指定scheme,那么整個(gè)URI的其他參數(shù)無(wú)效,這也意味著URI是無(wú)效的。
Host:URI的主機(jī)名,比如www.baidu.com,如果host未指定,那么整個(gè)URI中的其他參數(shù)無(wú)效,這也意味著URI是無(wú)效的。
Port:URI的端口號(hào),比如80,僅當(dāng)URI中指定了scheme和host參數(shù)的時(shí)候port參數(shù)才是有意義的。
Path、pathPattern和pathPrefix:這三個(gè)參數(shù)表述路徑信息,其中path表示完整的路徑信息;pathPattern也標(biāo)識(shí)完整的路徑信息,但是他里面可以包含通配符“”,“”標(biāo)識(shí)0個(gè)活多個(gè)字符,需要注意的是,由于正則表達(dá)式的規(guī)范,如果想標(biāo)識(shí)真實(shí)的字符串,那么“”要寫成“\*”,""要寫成“\\”;pathPrefix表示路徑的前綴信息。
0data匹配規(guī)則:要求intent中必須含有data數(shù)據(jù),并且data數(shù)據(jù)能夠完全匹配過(guò)濾規(guī)則中的某一個(gè)data,這里的完全匹配是指過(guò)濾規(guī)則中出現(xiàn)的data部分也出現(xiàn)在了intent中的data。
當(dāng)我們通過(guò)隱式方式啟動(dòng)一個(gè)activity的時(shí)候,可以做一下判斷,看是否有activity能夠匹配我們的隱式intent
判斷方式有兩種
- 采用PackageManager的resolveActivity方法或者Intent 的resolveActivity方法,如果他們找不到匹配的activity就會(huì)返回null。
- PackageManager還提供了queryIntentActivities方法,這個(gè)方法和resolveActivity方法不同的是,他不是返回最佳匹配的activity信息而是返回所有成功匹配的activity信息。
相關(guān)文章:android Activity生命周期
本文為本人學(xué)習(xí)筆,如有任何問(wèn)題,歡迎評(píng)論中指出,感激不盡!

推薦下本人的微信公眾號(hào),本博客及其他方面的消息會(huì)定期同步到公眾號(hào)上面!