- Android開發(fā)藝術(shù)探索
- 任玉剛
- 2815字
- 2019-01-10 17:37:36
1.3 IntentFilter的匹配規(guī)則
我們知道,啟動(dòng)Activity分為兩種,顯式調(diào)用和隱式調(diào)用。二者的區(qū)別這里就不多說了,顯式調(diào)用需要明確地指定被啟動(dòng)對(duì)象的組件信息,包括包名和類名,而隱式調(diào)用則不需要明確指定組件信息。原則上一個(gè)Intent不應(yīng)該既是顯式調(diào)用又是隱式調(diào)用,如果二者共存的話以顯式調(diào)用為主。顯式調(diào)用很簡(jiǎn)單,這里主要介紹一下隱式調(diào)用。隱式調(diào)用需要Intent能夠匹配目標(biāo)組件的IntentFilter中所設(shè)置的過濾信息,如果不匹配將無法啟動(dòng)目標(biāo)Activity。IntentFilter中的過濾信息有action、category、data,下面是一個(gè)過濾規(guī)則的示例:
<activity android:name="com.ryg.chapter_1.ThirdActivity" android:configChanges="screenLayout" android:label="@string/app_name" android:launchMode="singleTask" android:taskAffinity="com.ryg.task1" > <intent-filter > <action android:name="com.ryg.charpter_1.c"/> <action android:name="com.ryg.charpter_1.d"/> <category android:name="com.ryg.category.c"/> <category android:name="com.ryg.category.d"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter> </activity>
為了匹配過濾列表,需要同時(shí)匹配過濾列表中的action、category、data信息,否則匹配失敗。一個(gè)過濾列表中的action、category和data可以有多個(gè),所有的action、category、data分別構(gòu)成不同類別,同一類別的信息共同約束當(dāng)前類別的匹配過程。只有一個(gè)Intent同時(shí)匹配action類別、category類別、data類別才算完全匹配,只有完全匹配才能成功啟動(dòng)目標(biāo)Activity。另外一點(diǎn),一個(gè)Activity中可以有多個(gè)intent-filter,一個(gè)Intent只要能匹配任何一組intent-filter即可成功啟動(dòng)對(duì)應(yīng)的Activity,如下所示。
<activity android:name="ShareActivity"> <! -- This activity handles "SEND" actions with text data --> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter> <! -- This activity also handles "SEND" and "SEND_MULTIPLE" with media data --> <intent-filter> <action android:name="android.intent.action.SEND"/> <action android:name="android.intent.action.SEND_MULTIPLE"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="application/vnd.google.panorama360+jpg"/> <data android:mimeType="image/*"/> <data android:mimeType="video/*"/> </intent-filter> </activity>
下面詳細(xì)分析各種屬性的匹配規(guī)則。
1.action的匹配規(guī)則
action是一個(gè)字符串,系統(tǒng)預(yù)定義了一些action,同時(shí)我們也可以在應(yīng)用中定義自己的action。action的匹配規(guī)則是Intent中的action必須能夠和過濾規(guī)則中的action匹配,這里說的匹配是指action的字符串值完全一樣。一個(gè)過濾規(guī)則中可以有多個(gè)action,那么只要Intent中的action能夠和過濾規(guī)則中的任何一個(gè)action相同即可匹配成功。針對(duì)上面的過濾規(guī)則,只要我們的Intent中action值為“com.ryg.charpter_1.c”或者“com.ryg.charpter_ 1.d”都能成功匹配。需要注意的是,Intent中如果沒有指定action,那么匹配失敗。總結(jié)一下,action的匹配要求Intent中的action存在且必須和過濾規(guī)則中的其中一個(gè)action相同,這里需要注意它和category匹配規(guī)則的不同。另外,action區(qū)分大小寫,大小寫不同字符串相同的action會(huì)匹配失敗。
2.category的匹配規(guī)則
category是一個(gè)字符串,系統(tǒng)預(yù)定義了一些category,同時(shí)我們也可以在應(yīng)用中定義自己的category。category的匹配規(guī)則和action不同,它要求Intent中如果含有category,那么所有的category都必須和過濾規(guī)則中的其中一個(gè)category相同。換句話說,Intent中如果出現(xiàn)了category,不管有幾個(gè)category,對(duì)于每個(gè)category來說,它必須是過濾規(guī)則中已經(jīng)定義了的category。當(dāng)然,Intent中可以沒有category,如果沒有category的話,按照上面的描述,這個(gè)Intent仍然可以匹配成功。這里要注意下它和action匹配過程的不同,action是要求Intent中必須有一個(gè)action且必須能夠和過濾規(guī)則中的某個(gè)action相同,而category要求Intent可以沒有category,但是如果你一旦有category,不管有幾個(gè),每個(gè)都要能夠和過濾規(guī)則中的任何一個(gè)category相同。為了匹配前面的過濾規(guī)則中的category,我們可以寫出下面的Intent, intent.addcategory (“com.ryg.category.c”)或者Intent. addcategory (“com.ryg. category.d”)亦或者不設(shè)置category。為什么不設(shè)置category也可以匹配呢?原因是系統(tǒng)在調(diào)用startActivity或者startActivityForResult的時(shí)候會(huì)默認(rèn)為Intent加上“android.intent. category.DEFAULT”這個(gè)category,所以這個(gè)category就可以匹配前面的過濾規(guī)則中的第三個(gè)category。同時(shí),為了我們的activity能夠接收隱式調(diào)用,就必須在intent-filter中指定“android.intent.category.DEFAULT”這個(gè)category,原因剛才已經(jīng)說明了。
3.data的匹配規(guī)則
data的匹配規(guī)則和action類似,如果過濾規(guī)則中定義了data,那么Intent中必須也要定義可匹配的data。在介紹data的匹配規(guī)則之前,我們需要先了解一下data的結(jié)構(gòu),因?yàn)閐ata稍微有些復(fù)雜。
data的語法如下所示。
<data android:scheme="string" android:host="string" android:port="string" android:path="string" android:pathPattern="string" android:pathPrefix="string" android:mimeType="string" />
data由兩部分組成,mimeType和URI。mimeType指媒體類型,比如image/jpeg、audio/mpeg4-generic和video/*等,可以表示圖片、文本、視頻等不同的媒體格式,而URI中包含的數(shù)據(jù)就比較多了,下面是URI的結(jié)構(gòu):
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
這里再給幾個(gè)實(shí)際的例子就比較好理解了,如下所示。
content://com.example.project:200/folder/subfolder/etc http://www.baidu.com:80/search/info
看了上面的兩個(gè)示例應(yīng)該就瞬間明白了,沒錯(cuò),就是這么簡(jiǎn)單。不過下面還是要介紹一下每個(gè)數(shù)據(jù)的含義。
Scheme:URI的模式,比如http、file、content等,如果URI中沒有指定scheme,那么整個(gè)URI的其他參數(shù)無效,這也意味著URI是無效的。
Host:URI的主機(jī)名,比如www.baidu.com,如果host未指定,那么整個(gè)URI中的其他參數(shù)無效,這也意味著URI是無效的。
Port:URI中的端口號(hào),比如80,僅當(dāng)URI中指定了scheme和host參數(shù)的時(shí)候port參數(shù)才是有意義的。
Path、pathPattern和pathPrefix:這三個(gè)參數(shù)表述路徑信息,其中path表示完整的路徑信息;pathPattern也表示完整的路徑信息,但是它里面可以包含通配符“*”, “*”表示0個(gè)或多個(gè)任意字符,需要注意的是,由于正則表達(dá)式的規(guī)范,如果想表示真實(shí)的字符串,那么“*”要寫成“\\*”, “\”要寫成“\\\\”; pathPrefix表示路徑的前綴信息。
介紹完data的數(shù)據(jù)格式后,我們要說一下data的匹配規(guī)則了。前面說到,data的匹配規(guī)則和action類似,它也要求Intent中必須含有data數(shù)據(jù),并且data數(shù)據(jù)能夠完全匹配過濾規(guī)則中的某一個(gè)data.這里的完全匹配是指過濾規(guī)則中出現(xiàn)的data部分也出現(xiàn)在了Intent中的data中。下面分情況說明。
(1)如下過濾規(guī)則:
<intent-filter> <data android:mimeType="image/*" /> ... </intent-filter>
這種規(guī)則指定了媒體類型為所有類型的圖片,那么Intent中的mimeType屬性必須為“image/*”才能匹配,這種情況下雖然過濾規(guī)則沒有指定URI,但是卻有默認(rèn)值,URI的默認(rèn)值為content和file。也就是說,雖然沒有指定URI,但是Intent中的URI部分的schema必須為content或者file才能匹配,這點(diǎn)是需要尤其注意的。為了匹配(1)中規(guī)則,我們可以寫出如下示例:
intent.setDataAndType(Uri.parse("file://abc"), "image/png")。
另外,如果要為Intent指定完整的data,必須要調(diào)用setDataAndType方法,不能先調(diào)用setData再調(diào)用setType,因?yàn)檫@兩個(gè)方法彼此會(huì)清除對(duì)方的值,這個(gè)看源碼就很容易理解,比如setData:
public Intent setData(Uri data) { mData = data; mType = null; return this; }
可以發(fā)現(xiàn),setData會(huì)把mimeType置為null,同理setType也會(huì)把URI置為null。
(2)如下過濾規(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。為了匹配(2)中規(guī)則,我們可以寫出如下示例:
intent.setDataAndType(Uri.parse("http://abc"), "video/mpeg")
或者
intent. setDataAndType (Uri.parse("http://abc"), "audio/mpeg")
通過上面兩個(gè)示例,讀者應(yīng)該已經(jīng)明白了data的匹配規(guī)則,關(guān)于data還有一個(gè)特殊情況需要說明下,這也是它和action不同的地方,如下兩種特殊的寫法,它們的作用是一樣的:
<intent-filter . . . > <data android:scheme="file" android:host="www.baidu.com" /> . . . </intent-filter> <intent-filter . . . > <data android:scheme="file" /> <data android:host="www.baidu.com" /> . . . </intent-filter>
到這里我們已經(jīng)把IntentFilter的過濾規(guī)則都講解了一遍,還記得本節(jié)前面給出的一個(gè)intent-filter的示例嗎?現(xiàn)在我們給出完全匹配它的Intent:
Intent intent = new Intent("com.ryg.charpter_1.c"); intent.addCategory("com.ryg.category.c"); intent.setDataAndType(Uri.parse("file://abc"), "text/plain"); startActivity(intent);
還記得URI的schema是有默認(rèn)值的嗎?如果把上面的intent.setDataAndType (Uri.parse("file://abc"), "text/plain")這句改成intent.setDataAndType(Uri.parse("http://abc"),"text/plain"),打開Activity的時(shí)候就會(huì)報(bào)錯(cuò),提示無法找到Activity,如圖1-10所示。另外一點(diǎn),Intent-filter的匹配規(guī)則對(duì)于Service和BroadcastReceiver也是同樣的道理,不過系統(tǒng)對(duì)于Service的建議是盡量使用顯式調(diào)用方式來啟動(dòng)服務(wù)。

圖1-10 系統(tǒng)日志
最后,當(dāng)我們通過隱式方式啟動(dòng)一個(gè)Activity的時(shí)候,可以做一下判斷,看是否有Activity能夠匹配我們的隱式Intent,如果不做判斷就有可能出現(xiàn)上述的錯(cuò)誤了。判斷方法有兩種:采用PackageManager的resolveActivity方法或者Intent的resolveActivity方法,如果它們找不到匹配的Activity就會(huì)返回null,我們通過判斷返回值就可以規(guī)避上述錯(cuò)誤了。另外,PackageManager還提供了queryIntentActivities方法,這個(gè)方法和resolveActivity方法不同的是:它不是返回最佳匹配的Activity信息而是返回所有成功匹配的Activity信息。我們看一下queryIntentActivities和resolveActivity的方法原型:
public abstract List<ResolveInfo> queryIntentActivities(Intent intent, int flags); public abstract ResolveInfo resolveActivity(Intent intent, int flags);
上述兩個(gè)方法的第一個(gè)參數(shù)比較好理解,第二個(gè)參數(shù)需要注意,我們要使用MATCH_DEFAULT_ONLY這個(gè)標(biāo)記位,這個(gè)標(biāo)記位的含義是僅僅匹配那些在intent-filter中聲明了<category android:name="android.intent.category.DEFAULT"/>這個(gè)category的Activity。使用這個(gè)標(biāo)記位的意義在于,只要上述兩個(gè)方法不返回null,那么startActivity一定可以成功。如果不用這個(gè)標(biāo)記位,就可以把intent-filter中category不含DEFAULT的那些Activity給匹配出來,從而導(dǎo)致startActivity可能失敗。因?yàn)椴缓蠨EFAULT這個(gè)category的Activity是無法接收隱式Intent的。在action和category中,有一類action和category比較重要,它們是:
<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
這二者共同作用是用來標(biāo)明這是一個(gè)入口Activity并且會(huì)出現(xiàn)在系統(tǒng)的應(yīng)用列表中,少了任何一個(gè)都沒有實(shí)際意義,也無法出現(xiàn)在系統(tǒng)的應(yīng)用列表中,也就是二者缺一不可。另外,針對(duì)Service和BroadcastReceiver, PackageManager同樣提供了類似的方法去獲取成功匹配的組件信息。
- Docker進(jìn)階與實(shí)戰(zhàn)
- Java加密與解密的藝術(shù)
- 網(wǎng)店設(shè)計(jì)看這本就夠了
- C語言程序設(shè)計(jì)同步訓(xùn)練與上機(jī)指導(dǎo)(第三版)
- Java Web開發(fā)詳解
- 第一行代碼 C語言(視頻講解版)
- Visual Foxpro 9.0數(shù)據(jù)庫程序設(shè)計(jì)教程
- Kotlin開發(fā)教程(全2冊(cè))
- HoloLens與混合現(xiàn)實(shí)開發(fā)
- Qt 4開發(fā)實(shí)踐
- Visual C++從入門到精通(第2版)
- AutoCAD基礎(chǔ)教程
- 虛擬現(xiàn)實(shí)建模與編程(SketchUp+OSG開發(fā)技術(shù))
- Java程序設(shè)計(jì)及應(yīng)用開發(fā)
- 基于JavaScript的WebGIS開發(fā)