官术网_书友最值得收藏!

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同樣提供了類似的方法去獲取成功匹配的組件信息。

主站蜘蛛池模板: 东海县| 阿巴嘎旗| 漯河市| 揭阳市| 教育| 交口县| 湟中县| 剑川县| 宁明县| 灯塔市| 大化| 驻马店市| 行唐县| 高碑店市| 苏尼特右旗| 临沭县| 弥勒县| 肥城市| 黔南| 资源县| 图木舒克市| 应城市| 山丹县| 新宁县| 庄浪县| 墨玉县| 南涧| 高尔夫| 电白县| 晋城| 都昌县| 亳州市| 龙口市| 济源市| 陈巴尔虎旗| 通榆县| 钟山县| 镇安县| 邮箱| 班玛县| 峡江县|