2. Activity的啟動(dòng)模式
2.0 前言
本文總結(jié)自任玉剛老師的《Android開發(fā)藝術(shù)探索》,文章中的【示例】在這里
2.1 Activity的LaunchMode(啟動(dòng)模式)
任務(wù)棧簡(jiǎn)介:“后進(jìn)先出”的棧結(jié)構(gòu)副砍,每次back就會(huì)有一個(gè)Activity出棧衔肢,直到棧空為止豁翎,當(dāng)棧中無(wú)任何Activity時(shí)候角骤,系統(tǒng)就會(huì)回收這個(gè)任務(wù)棧。默認(rèn)情況下心剥,當(dāng)多次啟動(dòng)同一個(gè)Activity的時(shí)候邦尊,系統(tǒng)會(huì)創(chuàng)建多個(gè)實(shí)例并把它們一一放入任務(wù)棧中。
- (1) standard:標(biāo)準(zhǔn)模式优烧。也是系統(tǒng)的默認(rèn)模式蝉揍,每次啟動(dòng)一個(gè)Activity都會(huì)重新創(chuàng)建一個(gè)新的實(shí)例,不管這個(gè)實(shí)例是否已經(jīng)存在畦娄。被創(chuàng)建的實(shí)例的生命周期符合典型情況下的Activity的生命周期又沾。一個(gè)任務(wù)棧中可以有多個(gè)實(shí)例,每個(gè)實(shí)例也可以屬于不同的任務(wù)棧熙卡。在這種模式下杖刷,誰(shuí)啟動(dòng)了這個(gè)Activity,那么這個(gè)Activity就運(yùn)行在啟動(dòng)它的那個(gè)Activity所在的棧中再膳。比如Activity A啟動(dòng)了Activity B(B是標(biāo)準(zhǔn)模式)挺勿,那么B就會(huì)進(jìn)入A所在的棧中。
當(dāng)我們用ApplicationContext去啟動(dòng)standard模式的Activity的時(shí)候會(huì)報(bào)錯(cuò)喂柒,錯(cuò)誤如下:
E/AndroidRuntime(674): android.util.AndroidRuntimeException : Calling startActivity from outside of and Activity context requires the FLAG_ACTIVITY_NEW_TASK flag.Is this really what you want?
出現(xiàn)該錯(cuò)誤是因?yàn)閟tandard模式的Activity默認(rèn)會(huì)進(jìn)入啟動(dòng)它的Activity所屬的任務(wù)棧中不瓶,但是由于非Activity類型的Context(如ApplicationContext)并沒有所謂的任務(wù)棧,所以這就有問(wèn)題了灾杰。解決這個(gè)問(wèn)題的方法是為待啟動(dòng)的Activity指定FLAG_ACTIVITY_NEW_TASK標(biāo)記位蚊丐,這樣啟動(dòng)的時(shí)候就會(huì)為它創(chuàng)建一個(gè)新的任務(wù)棧,這個(gè)時(shí)候待啟動(dòng)Activity實(shí)際上已singleTask模式啟動(dòng)的艳吠。
(2) singleTop:棧頂復(fù)用模式麦备。這種模式下,如果新Activity已經(jīng)位于任務(wù)棧的棧頂昭娩,那么此Activity不會(huì)被重新創(chuàng)建凛篙,同時(shí)它的onNewIntent方法會(huì)被回調(diào),通過(guò)此方法的參數(shù)我們可以取出當(dāng)前請(qǐng)求的信息栏渺。需要注意的是呛梆,這個(gè)Activity的onCreate、onStart不會(huì)被系統(tǒng)調(diào)用磕诊,因?yàn)樗]有發(fā)生改變填物。如果新Activity的實(shí)例已存在但不是位于棧頂纹腌,那么新Activity仍然會(huì)重新創(chuàng)建。舉個(gè)例子滞磺,假設(shè)目前棧內(nèi)的情況為ABCD升薯,其中ABCD為4個(gè)Activity,A位于棧底击困,D位于棧頂涎劈,這個(gè)時(shí)候假設(shè)要再次啟動(dòng)D,如果D的啟動(dòng)模式為singleTop沛励,那么棧內(nèi)的情況仍然為ABCD责语;如果D的啟動(dòng)模式為standard,那么由于D被重新創(chuàng)建目派,導(dǎo)致棧內(nèi)的情況就變?yōu)锳BCDD。
(3) singleTask:棧內(nèi)復(fù)用模式胁赢。這是一種單實(shí)例模式企蹭,這種模式下。只要Activity在一個(gè)棧中存在智末,那么多次啟動(dòng)此Activity都不會(huì)重新創(chuàng)建實(shí)例谅摄,和singleTop一樣,系統(tǒng)也會(huì)回調(diào)其onNewIntent系馆。具體一點(diǎn)送漠,當(dāng)一個(gè)具有singleTask模式的Activity請(qǐng)求啟動(dòng)后,比如Activity A由蘑,系統(tǒng)首先會(huì)尋找是否存在A想要的任務(wù)棧闽寡,如果不存在,就重新創(chuàng)建一個(gè)任務(wù)棧尼酿,然后創(chuàng)建A的實(shí)例后把A放到棧中爷狈。如果存在A所需的任務(wù)棧,這是要看A是否在棧中有實(shí)例存在裳擎,如果有涎永,那么系統(tǒng)會(huì)把A調(diào)到棧頂并調(diào)用它的onNewIntent方法,如果實(shí)例不存在鹿响,就創(chuàng)建A的實(shí)例并把A壓入棧中羡微。
[ 實(shí)例(特殊情況):如果Activity D以singleTask模式請(qǐng)求啟動(dòng),其所需的任務(wù)棧為S1惶我,并且當(dāng)前任務(wù)棧S1的情況為ADBC妈倔,根據(jù)棧內(nèi)復(fù)用的原則,此時(shí)D不會(huì)被重新創(chuàng)建指孤,系統(tǒng)會(huì)把D切換到棧頂并調(diào)用其onNewIntent方法启涯,同時(shí)由于singleTask默認(rèn)具有clearTop的效果贬堵,會(huì)導(dǎo)致棧內(nèi)所有在D上面的Activity全部出棧,于是S1中的情況為AD结洼。 ]
singleTask示例1:[圖片上傳失敗...(image-63e361-1525671162550)]示例2:[圖片上傳失敗...(image-4d2023-1525671162550)]
【示例:singleTask的使用和adb的輸出情況(adb shell dumpsys activity),看Running activities(most recent first) 】
- (4)singleInstance:?jiǎn)螌?shí)例模式黎做。這是一種加強(qiáng)的singleTask模式,它除了具有singleTask模式的所有特性外松忍,還加強(qiáng)了一點(diǎn)蒸殿,那就是具有此種模式的Activity只能單獨(dú)位于一個(gè)任務(wù)棧中。比如Activity A是singleInstance模式鸣峭,當(dāng)A啟動(dòng)后宏所,系統(tǒng)會(huì)為它創(chuàng)建一個(gè)新的任務(wù)棧,然后A獨(dú)自在這個(gè)新的任務(wù)棧中摊溶,由于棧內(nèi)服用的特性爬骤,后續(xù)的請(qǐng)求均不會(huì)創(chuàng)建新的Activity,除非這個(gè)獨(dú)特的任務(wù)棧被系統(tǒng)銷毀了莫换。
指定啟動(dòng)模式的兩種方法(注意:第二種優(yōu)先級(jí) > 第一種霞玄;第一種無(wú)法直接為Activity設(shè)定FLAG_ACTIVITY_CLEAR_TOP表示娃惯,第二種無(wú)法為Activity指定singleIntance模式):
//(1)通過(guò)AndroidMenifest
<activity
android:name=".FifthActivity"
android:launchMode="standard"/>
//(2)通過(guò)Intent中指定標(biāo)志位
Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
2.1.1 TaskAffinity
在singleTask啟動(dòng)模式中絮识,多次提到某個(gè)Activity所需的任務(wù)棧。究竟什么是Activity所需的任務(wù)棧辖试,要從TaskAffinity(任務(wù)相關(guān)性)說(shuō)起喊暖,這個(gè)參數(shù)標(biāo)識(shí)了一個(gè)Activity所需要的任務(wù)棧的名字惫企,默認(rèn)情況下,所有Activity所需的任務(wù)棧的名字為應(yīng)用的包名陵叽。當(dāng)然我們可以為每個(gè)Activity都單獨(dú)制定TaskAffinity屬性狞尔,這個(gè)屬性值不能和包名相同,否則就相當(dāng)于沒有指定咨跌。
TaskAffinity屬性主要和singleTask啟動(dòng)模式或者allowTaskReparenting屬性配對(duì)使用沪么,在其他情況下沒有任何意義。另外锌半,任務(wù)棧分為前臺(tái)任務(wù)棧和后臺(tái)任務(wù)棧禽车,后臺(tái)任務(wù)棧中的Activity處于暫停狀態(tài),用戶可以通過(guò)切換將后臺(tái)任務(wù)棧再次調(diào)到前臺(tái)刊殉。
當(dāng)TaskAffinity和singleTask啟動(dòng)模式配對(duì)使用的時(shí)候殉摔,它是具有該模式的Activity的目前任務(wù)棧的名字,待啟動(dòng)的Activity會(huì)運(yùn)行在名字和TaskAffinity相同的任務(wù)棧中记焊。
當(dāng)TaskAffinity和allowTaslReparenting結(jié)合的時(shí)候逸月,這種情況較復(fù)雜,產(chǎn)生特殊的效果:當(dāng)一個(gè)應(yīng)用A啟動(dòng)了應(yīng)用B的某個(gè)Activity C后遍膜,如果這個(gè)Activity C的allowTaskReparenting( Reparent:重定父級(jí))屬性為true的話碗硬,那么當(dāng)應(yīng)用B被啟動(dòng)后瓤湘,此Activity會(huì)直接從應(yīng)用A的任務(wù)棧轉(zhuǎn)移到應(yīng)用B的任務(wù)棧中。
[ 解釋:由于A啟動(dòng)了C恩尾,這個(gè)時(shí)候C只能運(yùn)行在A的任務(wù)棧中弛说,但是C屬于B應(yīng)用,正常情況下它的TaskAffinity值肯定不可能與A的任務(wù)棧相同(因?yàn)榘煌┖惨狻K阅救耍?dāng)B被啟動(dòng)后,B會(huì)創(chuàng)建自己的任務(wù)棧冀偶,這個(gè)時(shí)候系統(tǒng)發(fā)現(xiàn)C原本想要的任務(wù)棧已經(jīng)被創(chuàng)建了醒第,所以就把C從A的任務(wù)棧中轉(zhuǎn)移過(guò)來(lái)了。 ]
- (1) 設(shè)定了android:launchMode="singleTask"的SixthActivity 連續(xù)6次用startActivity(intent)連續(xù)自己?jiǎn)?dòng)自己3次:
//在cmd中執(zhí)行adb shell dumpsys activity后輸出的結(jié)果
Running activities (most recent first):
TaskRecord{2b9827e #383 A=com.example.learn_001_activity U=0 StackId=1 sz=2}
Run #4: ActivityRecord{e99eb46 u0 com.example.learn_001_activity/.SixthActivity t383}
Run #3: ActivityRecord{7a5d672 u0 com.example.learn_001_activity/.MainActivity t383}
TaskRecord{5ac339a #299 A=com.android.gallery3d U=0 StackId=1 sz=1}
Run #2: ActivityRecord{4df2ed7 u0 com.android.gallery3d/.app.GalleryActivity t299}
TaskRecord{52714cb #278 A=com.example.demo103 U=0 StackId=1 sz=2}
Run #1: ActivityRecord{274a48f u0 com.example.demo103/.MainBindActivity t278}
Run #0: ActivityRecord{e70354a u0 com.example.demo103/.MainGeetestActivity t278}
mResumedActivity: ActivityRecord{e99eb46 u0 com.example.learn_001_activity/.SixthActivity t383}
可以看出前臺(tái)任務(wù)棧的taskAffinity值為com.example.learn_001_activity(另外2個(gè)是其他沒關(guān)的程序的)进鸠,它里面只有1個(gè)Activity
//SixthActivity中復(fù)寫的onNewIntent方法:SixthActivity.onNewIntent()
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d(TAG, "onNewIntent, time = " + intent.getLongExtra("time", 0));
}
//Logcat中輸出的結(jié)果
03-14 09:21:53.261 1798-1798/com.example.learn_001_activity D/SixthActivity: onPause
03-14 09:21:53.261 1798-1798/com.example.learn_001_activity D/SixthActivity: onNewIntent, time = 1521019313259
03-14 09:21:53.262 1798-1798/com.example.learn_001_activity D/SixthActivity: onResume
03-14 09:21:53.835 1798-1798/com.example.learn_001_activity D/SixthActivity: onPause
03-14 09:21:53.835 1798-1798/com.example.learn_001_activity D/SixthActivity: onNewIntent, time = 1521019313832
03-14 09:21:53.835 1798-1798/com.example.learn_001_activity D/SixthActivity: onResume
03-14 09:21:54.433 1798-1798/com.example.learn_001_activity D/SixthActivity: onPause
03-14 09:21:54.433 1798-1798/com.example.learn_001_activity D/SixthActivity: onNewIntent, time = 1521019314430
03-14 09:21:54.433 1798-1798/com.example.learn_001_activity D/SixthActivity: onResume
可以看出稠曼,Activity的確沒有重新創(chuàng)建,只是暫停了一下堤如,然后調(diào)用了onNewIntent蒲列,接著調(diào)用onResume就又繼續(xù)了。
- (2)去掉android:launchMode="singleTask"后的SixthActivity搀罢,再執(zhí)行上述操作:
Running activities (most recent first):
TaskRecord{5c0bdbe #384 A=com.example.learn_001_activity U=0 StackId=1 sz=5}
Run #7: ActivityRecord{48e5526 u0 com.example.learn_001_activity/.SixthActivity t384}
Run #6: ActivityRecord{f76fb50 u0 com.example.learn_001_activity/.SixthActivity t384}
Run #5: ActivityRecord{1afc095 u0 com.example.learn_001_activity/.SixthActivity t384}
Run #4: ActivityRecord{3363da1 u0 com.example.learn_001_activity/.SixthActivity t384}
Run #3: ActivityRecord{50cd266 u0 com.example.learn_001_activity/.MainActivity t384}
TaskRecord{5ac339a #299 A=com.android.gallery3d U=0 StackId=1 sz=1}
Run #2: ActivityRecord{4df2ed7 u0 com.android.gallery3d/.app.GalleryActivity t299}
TaskRecord{52714cb #278 A=com.example.demo103 U=0 StackId=1 sz=2}
Run #1: ActivityRecord{274a48f u0 com.example.demo103/.MainBindActivity t278}
Run #0: ActivityRecord{e70354a u0 com.example.demo103/.MainGeetestActivity t278}
可以看出前臺(tái)任務(wù)棧的taskAffinity值為com.example.learn_001_activity(另外2個(gè)是其他沒關(guān)的程序的),它里面有4個(gè)Activity
Running activities (most recent first):
TaskRecord{7e50489 #106 I=com.google.android.apps.nexuslauncher/.NexusLauncherActivity U=0 StackId=0 sz=1}
Run #1: ActivityRecord{b8d40e4 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t106}
TaskRecord{9fbe4a8 #111 A=com.android.systemui U=0 StackId=0 sz=1}
Run #0: ActivityRecord{f63f216 u0 com.android.systemui/.recents.RecentsActivity t111}
還有這兩個(gè)應(yīng)該就是后臺(tái)任務(wù)棧了侥猩,其taskAffinity值為com.google.android.apps.nexuslauncher和com.android.systemui
至于singleTask模式的Activity切換到棧頂會(huì)使在它之上的棧內(nèi)的Activity出棧榔至,這里就不演示了。
2.2 Activity的flags
- FLAG_ACTIVITY_NEW_TASK :指定"singleTask"啟動(dòng)模式欺劳,其效果和在XML中指定該啟動(dòng)模式相同唧取。
- FLAG_ACTIVITY_SINGLE_TOP :指定"singleTask"啟動(dòng)模式,其效果和在XML中指定該啟動(dòng)模式相同划提。
- FLAG_ACTIVITY_CLEAR_TOP :具有此標(biāo)記位的Activity枫弟,當(dāng)它啟動(dòng)時(shí),在同一個(gè)任務(wù)棧中所有位于它上面的Activity都要出棧鹏往,這個(gè)模式一般需要和FLAG_ACTIVITY_NEW_TASK 配合使用淡诗,在這種情況下,被啟動(dòng)的Activity的實(shí)例如果已經(jīng)存在伊履,那么系統(tǒng)就會(huì)調(diào)用它的onNewIntent韩容。如果被啟動(dòng)的Activity采用standard模式啟動(dòng),那么它連同連同它之上的Activity都要出棧唐瀑,系統(tǒng)會(huì)創(chuàng)建新的Activity實(shí)例并放入棧頂群凶。singleTask啟動(dòng)模式默認(rèn)具有此標(biāo)記位的效果。
- FLAG_ACTIVITY_EXCLUDE_FEOM_RECENTS :具有該標(biāo)記的Activity不會(huì)出現(xiàn)在歷史Activity的列表中哄辣,请梢。等同于XML中指定Activity的屬性android:excludeFromRecents="true"
2.3 IntentFilter的匹配規(guī)則
啟動(dòng)Activity分為2種:顯式調(diào)用(明確指定被啟動(dòng)對(duì)象的組件信息:包括包名和類名)和隱式調(diào)用(不需要明確指定被啟動(dòng)對(duì)象的組件信息)赠尾,如果二者共存則以顯示調(diào)用為主。
關(guān)于隱式調(diào)用毅弧,即需要Intent能夠匹配目標(biāo)組件的IntentFilter中所設(shè)置的過(guò)濾信息气嫁,如果不匹配將無(wú)法啟動(dòng)目標(biāo)Activity。IntentFilter中的過(guò)濾信息有action形真、category杉编、data。
//過(guò)濾規(guī)則的示例:
<activity
android:name=".SeventhActivity"
android:launchMode="singleTask"
android:taskAffinity="com.example.learn_001_activity">
<intent-filter>
<action android:name="com.example.learn_001_activity.c"/>
<action android:name="com.example.learn_001_activity.d"/>
<category android:name="com.example.category.c"/>
<category android:name="com.example.category.d"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
一個(gè)Activity中可以有多個(gè)intent-filter(匹配列表咆霜,其中的過(guò)濾信息有action邓馒、category、data(可以有多個(gè)))蛾坯,為了匹配過(guò)濾列表光酣,一個(gè)Intent需要同時(shí)匹配列表中的action、category脉课、data信息救军,才能成功啟動(dòng)目標(biāo)Activity。
(1) action 的匹配規(guī)則:
action是一個(gè)字符串倘零,系統(tǒng)預(yù)定義了一些action唱遭,同時(shí)我們可以在應(yīng)用中定義自己的action。action的匹配規(guī)則是Intent中的action必須和過(guò)濾規(guī)則中的action匹配(區(qū)分大小寫呈驶,字符串值完全一樣)拷泽。一個(gè)過(guò)濾規(guī)則(即intent-filter、匹配列表)可以有多個(gè)action袖瞻,那么只要Intent中的action能夠和過(guò)濾規(guī)則中的任何一個(gè)action相同即可匹配成功司致。針對(duì)上面例子的過(guò)濾規(guī)則,只要我們的Intent中action值為"com.example.learn_001_activity.c"或者"com.example.learn_001_activity.d"就能成功匹配聋迎,注意脂矫,Intent中如果沒有指定action,那么匹配失敗霉晕。(2) category 的匹配規(guī)則:
category是一個(gè)字符串庭再,系統(tǒng)預(yù)定義了一些category,同時(shí)我們可以在應(yīng)用中定義自己的category娄昆。與action不同的是佩微,category要求Intent可以沒有category(系統(tǒng)在startActivity或startActivityForResult的時(shí)候默認(rèn)Ietent加上"android.intent.category.DEFAULT"),但是如果你一旦有了category萌焰,不管有幾個(gè)哺眯,每個(gè)都要能過(guò)和過(guò)濾規(guī)則中的任何一個(gè)category相同。針對(duì)上面例子的過(guò)濾規(guī)則扒俯,只要我們寫下面的Intent:intent.addcategory("com.example.learn_001_activity.c"或intent.addcategory("com.example.learn_001_activity.d")就能成功匹配奶卓。(3) data 的匹配規(guī)則:
data的匹配規(guī)則和action類似一疯,如果過(guò)濾規(guī)則中定義了data,那么Intent中必須也要定義可匹配的data夺姑。
//data的語(yǔ)法:
<data
android:scheme="string"
android:host="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string"/>
data由兩部分組成:mimeType和URI墩邀。mimeType指媒體類型,比如image/jepg盏浙、audio/mpeg4-generic和video/*等眉睹,可以表示圖片、文本废膘、視頻等不同的媒體格式竹海,而URI中包含的數(shù)據(jù)就比較多了,下面是URI的結(jié)構(gòu):
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
//下面兩個(gè)例子:
content://com.example.project:200/folder/subfolder/etc
http://www/baidu.com:80/search/info
至于每個(gè)數(shù)據(jù)的含義如下:
1:Scheme:URI的模式丐黄,比如http斋配、file、content等灌闺,如果URI中沒有指定scheme艰争,那么整個(gè)URI的其他參數(shù)無(wú)效,這也意味著URI是無(wú)效的桂对。
2:Host:URI的主機(jī)名甩卓,比如www.baidu.com,如果host未指定蕉斜,那么整個(gè)URI中的其他參數(shù)無(wú)效猛频,也意味著URI是無(wú)效的。
3:Port:URI的端口號(hào)蛛勉,比如80,僅當(dāng)URI中制定了scheme和host參數(shù)的時(shí)候port參數(shù)才是有意義的睦柴。
4:path诽凌、pathPattern和pathPrefix;這3個(gè)參數(shù)表述路徑信息坦敌。path表示完整的路徑信息侣诵;pathPattern也表示完整的路徑信息,但是它里面可以包含通配符“ * ”狱窘,“ * ”表示0個(gè)或多個(gè)任意字符杜顺,注意,由于正則表達(dá)式的規(guī)范蘸炸,如果想表示真實(shí)的字符串躬络,“ * ”要寫成“ \\* ”,“ \ ”要寫成“ \\\\ ”搭儒;pathPrefix表示路徑的前綴信息穷当。
前面說(shuō)到提茁,data的匹配規(guī)則和action類似,它要求Intent中必須含有data數(shù)據(jù)馁菜,并且data數(shù)據(jù)能夠完全匹配過(guò)濾規(guī)則中的某一個(gè)data茴扁。下面分情況說(shuō)明:
(1) 如下過(guò)濾規(guī)則:
<intent-filter>
<data android:mimeType="image/*"/>
.....
</intent-filter>
這種規(guī)則制定了媒體類型為所有類型的圖片,那么Intent中的mimeType屬性必須為“image/”才能匹配汪疮,這種情況下雖然過(guò)濾規(guī)則中沒有指定URI峭火,但是卻有默認(rèn)值:content和file。也就是說(shuō)智嚷,雖然沒有指定URI卖丸,但是Intent中的URI部分的schema必須為content或file才能匹配*。為了匹配(1)中規(guī)則纤勒,可以寫出如下示例(如果要為Intent指定完整的data坯苹,必須調(diào)用setDataAndType方法,不能先調(diào)用setData再調(diào)用setType摇天,因?yàn)檫@兩個(gè)方法會(huì)彼此清除對(duì)方的值粹湃。):
intent.setDataAndType(Uri.parse("file://abc"), "image/png");
(2)如下過(guò)濾規(guī)則:
<intent-filter>
<data android:mimeType="video/mpeg" android:scheme="http" .../>
<data android:mimeType="audio/mpeg" android:scheme="http" .../>
.....
</intent-filter>
這種規(guī)則指定了兩組data規(guī)則,且每個(gè)data都指定了完整的屬性值泉坐,既有URI又有mimeType为鳄。為了匹配(1)中規(guī)則,可以寫出如下示例:
intent.setDataAndType(Uri.parse("http://abc"), "video/mpeg");
或者
intent.setDataAndType(Uri.parse("http://abc"), "audio/mpeg");
對(duì)于一開始給出的intent-filter的示例腕让,現(xiàn)在我們給出完全匹配它的Intent:
Intent intent = new Intent("com.example.learn_001_activity.c");
intent.addCategory("com.example.category.c");
intent.setDataAndType(Uri.parse("file://abc"), "text/plain"); //手機(jī)里面要有名字是abc的file文件...
startActivity(intent);
上面說(shuō)到孤钦,URI的schema是由默認(rèn)值的(content和file),如果把上面的intent.setDataAndType(Uri.parse("file://abc"), "text/plain");改成intent.setDataAndType(Uri.parse("http://abc"), "text/plain");纯丸,打開Activity的時(shí)候就會(huì)報(bào)錯(cuò)偏形,提示無(wú)法找到Activity。另外Intent-filter的匹配規(guī)則對(duì)于Service和BroadcastReceiver也是同樣的道理觉鼻,不過(guò)系統(tǒng)對(duì)于Service的建議是盡量使用顯示調(diào)用方式來(lái)啟動(dòng)服務(wù)俊扭。
最后,當(dāng)我們想通過(guò)隱式方式啟動(dòng)一個(gè)Activity的時(shí)候坠陈,可以做一下判斷萨惑,看是否有Activity能夠匹配我們的隱式Intent,判斷方法有2種:采用PackageManager的resolveActivity方法或者Intent的resolveActivity方法仇矾,如果找不到匹配的Activity就會(huì)返回null庸蔼,通過(guò)判斷返回值就可以規(guī)避上述錯(cuò)誤了。另外贮匕,PackageManager還提供了queryIntentActivities方法:返回所有成功匹配的Activity信息姐仅。
//PackageManager類中:
public abstract List<ResolveInfo> queryIntentActivities(Intent intent, int flags);
public abstract ResolveInfo resolveActivity(Intent intent, int flags);
上述兩個(gè)方法的第二個(gè)參數(shù)需要注意:我們要使用MATCH_DEFAULT_ONLY這個(gè)標(biāo)記位,這個(gè)標(biāo)記位的含義是僅僅匹配那些在intent-filter中聲明了<category android:name="android.iintent.category.DEFAULT"/>這個(gè)category的Activity。使用這個(gè)標(biāo)記位的意義在于萍嬉,只要上述兩個(gè)方法不返回null乌昔,那么startActivity一定可以成功,如果不用這個(gè)標(biāo)記位壤追,就可以吧intent-filter中category不含DEFAULT的那些Activity匹配出來(lái)磕道,從而導(dǎo)致startActivity可能失敗。因?yàn)椴缓蠨EFAULT這個(gè)category的Activity是否無(wú)法接收隱式Intent的行冰。
在action和category中有一類action和category比較重要:
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
這二者的共同作用是來(lái)標(biāo)明這是一個(gè)入口Activity并且會(huì)出現(xiàn)在系統(tǒng)的應(yīng)用列表中溺蕉。另外,針對(duì)Service和BroadcastReceiver悼做,PackageManager同樣提供了類似的犯法去獲取成功匹配的組件信息疯特。