Android開發藝術探索知識回顧——第1章 Activity的生命週期和啓動模式:3、IntentFilter的匹配規則 action、category、data

 

1.3 IntentFilter的匹配規則

我們知道,啓動 Activity 分爲兩種,顯式調用和隱式調用。二者的區別這裏就不多說,顯式調用需要明確地指定被啓動對象的組件信息,包括包名和類名,而隱式調用則不需要明確指定組件信息。原則上一個 Intent 不應該既是顯式調用又是隱式調用,如果二者共存的話以顯式調用爲主

顯式調用很簡單,這裏主要介紹一下隱式調用。隱式調用需要 Intent 能夠匹配目標組件的 IntentFilter 中所設置的過濾信息,如果不匹配將無法啓動目標 Activity。IntentFilter 中的過濾信息有 action、category、data,下面是一個過濾規則的示例:

        <activity
            android:name="com.yyh.demo6.IntentFilter.ThirdActivity"
            android:configChanges="screenLayout"
            android:label="@string/app_name"
            android:taskAffinity="com.ryg.task1" 
            android:launchMode="singleTask">
            <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>

爲了匹配過濾列表,需要同時匹配過濾列表中的 action、category、data 信息,否則匹配失敗。一個過濾列表中的 action、category 和data 可以有多個,所有的 action、category、 data 分別構成不同類別,同一類別的信息共同約束當前類別的匹配過程。

只有一個 Intent 同時匹配action類別、category類別、data類別纔算完全匹配,只有完全匹配才能成功啓動目標 Activity。另外一點,一個 Activity 中可以有多個 intent-filter,一個 Intent 只要能匹配任何一組 intent-filter 即可成功啓動對應的 Activity,如下所示。

         <activity android:name="ShareActivity">
            <!-- This activity handlers "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 handlers "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>

 

下面詳細分析各種屬性的匹配規則。

1. action的匹配規則

action 是一個字符串,系統預定義了一些 action,同時我們也可以在應用中定義自己的 action。action 的匹配規則是 Intent 中的 action 必須能夠和過濾規則中的 action 匹配,這裏說的匹配是指 action 的字符串值完全一樣。

一個過濾規則中可以有多個 action,那麼只要 Intent 中的 action 能夠和過濾規則中的任何一個 action 相同即可匹配成功。針對上面的過濾規則,只要我們的 Intent 中 action 值爲 “com.ryg.charpter_l.c” 或者 “com.ryg.charpter_ l.d” 都能成功匹配。需要注意的是,Intent中如果沒有指定 action,那麼匹配失敗。

總結一下,action的匹配要求 Intent 中的 action 存在且必須和過濾規則中的其中一個 action 相同,這裏需要注意它和 category 匹配規則的不同。另外,action 區分大小寫,大小寫不同字符串相同的 action 會匹配失敗。

 

2. category的匹配規則

category 是一個字符串,系統預定義了一些 category,同時我們也可以在應用中定義自己的 category。category 的匹配規則和 action 不同,它要求 Intent 中如果含有 category,那麼所有的 category 都必須和過濾規則中的其中一個 category 相同。換句話說,Intent 中如果出現了 category,不管有幾個category,對於每個category來說,它必須是過濾規則中已經定義了的 category。

當然,Intent中可以沒有 category,如果沒有 category 的話,按照上面的描述,這個 Intent 仍然可以匹配成功。這裏要注意下它和 action 匹配過程的不同,action 是要求 Intent 中必須有一個 action 且必須能夠和過濾規則中的某個 action 相同,而 category 要求 Intent 可以沒有category,但是如果你一旦有 category,不管有幾個,每個都要能夠和過濾規則中的任何一個 category 相同。

爲了匹配前面的過濾規則中的 category,我們可以寫出下面的 Intent,intent.addcategory ("com.ryg.category.c") 或者 Intent.addcategory ("com.ryg. category.d")  亦或者不設置 category。

爲什麼不設置 category 也可以匹配呢?

原因是系統在調用 startActivity 或者 startActivityForResult 的時候會默認爲 Intent 加上 " android.intent.category.DEFAULT " 這個 category,所以這個 category 就可以匹配前面的過濾規則中的第三個 category。同時,爲了我們的activity能夠接收隱式調用,就必須在 intent-filter 中指定 "android.intent.category.DEFAULT" 這個 category,原因剛纔已經說明了。

 

3. data的匹配規則

data 的匹配規則和 action 類似,如果過濾規則中定義了 data,那麼 Intent 中必須也要定義可匹配的 data。在介紹 data 的匹配規則之前,我們需要先了解一下 data 的結構,因爲 data 稍微有些複雜。

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 中包含的數據就比較多了,下面是 URI 的結構:

    <scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]

這裏再給幾個實際的例子就比較好理解了,如下所示。
 

content://com.example.project:200/folder/subfolder/etc 

http://www.baidu.com:80/search/info

看了上面的兩個示例應該就瞬間明白了,沒錯,就是這麼簡單。不過下面還是要介紹 一下每個數據的含義。

Scheme:URI的模式,比如 http、file、content 等,如果URI中沒有指定 scheme。那 麼整個 URI 的其他參數無效,這也意味着 URI 是無效的。

Host:URI的主機名,比如 www.baidu.com,如果 host 未指定,那麼整個URI中的其他參數無效,這也意味着 URI 是無效的。

Port:URI中的端口號,比如80,僅當 URI 中指定了 scheme 和 host 參數的時候 port 參數纔是有意義的。

Path、pathPattem 和 pathPrefix:這三個參數表述路徑信息,其中 path 表示完整的路徑信息;pathPattem 也表示完整的路徑信息,但是它裏面可以包含通配符 "*","*" 表示 0 個或多個任意字符,需要注意的是,由於正則表達式的規範,如果想表示真實的字符串,那麼"*"要寫成 "\\*","\"要寫成"\\\\";pathPrefix表示路徑的前綴信息。

介紹完 data 的數據格式後,我們要說一下 data 的匹配規則了。前面說到,data 的匹配規則和 action 類似,它也要求 Intent 中必須含有 data 數據,並且 data 數據能夠完全匹配過濾規則中的某一個 data。這裏的完全匹配是指過濾規則中出現的 data 部分也出現在了 Intent 中的 data 中。下面分情況說明。

(1)如下過濾規則:

       <intent-filter>
          <data android:mimeType="image/*"/>
      </intent-filter>

這種規則指定了媒體類型爲所有類型的圖片,那麼 Intent 中的 mimeType 屬性必須爲 "image/*" 才能匹配,這種情況下雖然過濾規則沒有指定 URI,但是卻有默認值,URI 的默認值爲 content 和 file 。

也就是說,雖然沒有指定 URI,但是 Intent 中的 URI 部分的 schema 必須爲 content 或者 file 才能匹配,這點是需要尤其注意的。爲了匹配 (1) 中規則,我們可以寫出如下示例:

    intent.setDataAndType(Uri.parse("file://abc"),"image/png");

另外,如果要爲 Intent 指定完整的 data,必須要調用 setDataAndType 方法,不能先調用 setData 再調用 setType,因爲這兩個方法彼此會清除對方的值,這個看源碼就很容易理解,比如setData:

    public Intent setData(Uri data) {
        mData = data;
        mType = null;
        return this;
    }

可以發現,setData 會把 mimeType 置爲null,同理 sctType 也會把 URI 置爲 null。

 

(2)如下過濾規則:

      <intent-filter>
           <data android:mimeType="video/mpeg" android:scheme="http" .../>
           <data android:mimeType="audio/mpeg" android:scheme="http" .../>
      </intent-filter>

這種規則指定了兩組 data 規則,且每個 data 都指定了完整的屬性值,既有 URI 又有 mimeType。爲了匹配 (2) 中規則,我們可以寫岀如下示例:

    intent.setDataAndType(Uri.parse("http://abc"),"video/png");

或者

    intent.setDataAndType(Uri.parse("http://abc"),"audio/png"); 

通過上面兩個示例,讀者應該已經明白了 data 的匹配規則,關於 data 還有一個特殊情況需要說明下,這也是它和 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>

到這裏我們已經把 IntentFilter 的過濾規則都講解了一遍,還記得本節前面給出的一個 intent-filter 的示例嗎?現在我們給出完全匹配它的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 是有默認值的嗎?

如果把上面的 intent.setDataAndType(Uri.parse("file://abc"), text/plain") 這句成 intent.setDataAndType(Uri.parse("http://abc"), "text/plain")打開 Activity 的時候就會報錯,提示無法找到 Activity,如圖1-10所示。

另外 一點,Intent-filter的匹配規則對於Service和BroadcastReceiver 也是同樣的道理,不過系統對於 Service 的建議是儘量使用顯式調用方式來啓動服務。

 

最後,當我們通過隱式方式啓動一個 Activity 的時候,可以做一下判斷,看是否有 Activity 能夠匹配我們的隱式 Intent,如果不做判斷就有可能出現上述的錯誤了。判斷方法有兩種:釆用 PackageManager 的 resolveActivity 方法或者 Intent 的 resoIveActivity 方法,如果它們找不到匹配的 Activity 就會返回 null,我們通過判斷返回值就可以規避上述錯誤了。

另外,PackageManager 還提供了 querylntentActivities 方法,這個方法和 resolveActivity 方法不同的是:它不是返回最佳匹配的 Activity信息而是返回所有成功匹配的 Activity 信息。 我們看一下 querylntentActivities 和 resolveActivity 的方法原型:

    public abstract List<ResolveInfo>queryIntentActivities(Intent intent,int fladgs);
    public abstract ResolveInfo resolveActivity(Intent intent,int flags);

上述兩個方法的第一個參數比較好理解,第二個參數需要注意,我們要使用 MATCH_ DEFAULT_ONLY 這個標記位,這個標記位的含義是僅僅匹配那些在 intent-filter 中聲明瞭 <category android:name="android.intent.category.DEFAULT"/> 這個 category 的 Activity。

使用這個標記位的意義在於,只要上述兩個方法不返回null,那麼 startActivity 一定可以成功。 如果不用這個標記位,就可以把intent-filter中 category 不含 DEFAULT 的那些 Activity 給匹配出來,從而導致 startActivity 可能失敗。因爲不含有 DEFAULT 這個 category 的 Activity 是無法接收隱式 Intent 的。在 action 和 category 中,有一類 action 和 category 比較重要,它們是:

    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />

這二者共同作用是用來標明這是一個入口 Activity 並且會出現在系統的應用列表中,少了任何一個都沒有實際意義,也無法出現在系統的應用列表中,也就是二者缺一不可。 另外,針對 Service 和 BroadcastReceiver,PackageManager 同樣提供了類似的方法去獲取成功匹配的組件信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章