Android中Intent物件與Intent Filter過濾匹配過程詳解
如果對Intent不是特別瞭解,可以參見博文《Android中Intent概述及使用》,該文對本文要使用的action、category以及data都進行了詳細介紹。如果想了解在開發中常見Intent的使用,可以參見《Android中常見Intent習慣用法-上篇(附原始碼下載)》。
本文內容有點長,希望大家可以耐心讀完。
本文在描述元件在manifest中註冊的Intent Filter過濾器時,統一用intent-filter表示。
概述
我們知道,Intent是分兩種的:顯式Intent和隱式Intent。如果一個Intent明確指定了要啟動的元件的完整類名,那麼這個Intent就是顯式Intent,否則就是隱式Intent。當我們用一個顯式Intent去啟動元件時,Android會根據Intent物件所提供的component name直接找到要啟動的元件,當我們用一個隱式的Intent去啟動元件時,Android系統就無法直接知道要啟動的元件名稱了,本文就是講解Android系統如何根據隱式Intent查詢匹配到要啟動的元件。
當Android系統接收到一個隱式Intent要啟動一個Activity(或其他元件)時,Android會根據以下三個資訊比較Intent的資訊與註冊的元件的intent-filter的資訊,從而為該Intent選擇出最匹配的Activity(或其他元件):
- intent中的action
- intent中的category
- intent中的data(包含Uri以及data的MIME型別)
也就是隱式intent物件要滿足要啟動的目標元件中註冊的intent-filter中的<action />
、<category />
、<data />
三個標籤中的資訊,即要分別通過action測試、category測試以及data測試。intent-filter資訊是在Android的manife檔案中描述的,顧名思義,intent-filter是intent過濾器,就是用來過濾intent的。
如果隱式intent物件同時通過了某個元件的中intent-filter的action測試、category測試以及data測試,那麼該元件就可以被intent物件所啟動。如果隱式intent物件沒有通過系統中任何元件的intent-filter測試,那麼就沒有Android系統無法找到該intent物件要啟動的元件。下面我們依次看一下如何才能通過這三個測試。
Action測試
為了指定能夠接收並處理的Intent的型別,元件可以在intent-filter中宣告其支援0個或多個action,例如:
<intent-filter>
<action android:name="com.ispring.action.ACTION_TEST1" />
<action android:name="com.ispring.action.ACTION_TEST2" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
intent物件可以通過setAction()方法設定唯一的一個action值。對於action測試,需要分兩種情況:
intent物件設定了action
如果intent物件通過呼叫setAction()方法設定了action的值,那麼只有當元件的intent-filter中包含了intent物件中的action值的時候,action測試才通過,否則無法通過。
舉個例子,假設我們的Activity的intent-filter如下所示:<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <action android:name="com.ispring.action.ACTION_TEST2" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> </intent-filter>
下面的intent物件可以通過上面intent-filter裡面的action測試:
Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1"); Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun"); intent.setData(uri);
該intent之所以能通過action測試是因為intent-filter中包含該intent的action值com.ispring.action.ACTION_TEST1。
下面的intent物件無法通過上面intent-filter裡面的action測試:
Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST3"); Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun"); intent.setData(uri);
該intent之所以無法通過action測試是因為intent-filter中不包含該intent的action值com.ispring.action.ACTION_TEST3。
intent物件沒有設定action
如果intent物件沒有呼叫setAction()方法設定action的值,那麼如果intent-filter至少有一個任意的action的值,該intent物件就可以通過該intent-filter的action測試,反之,如果intent-filter中沒有定義任何的action,那麼該intent無法通過該intent-filter的action測試。
舉個例子,假設我們的intent物件如下所示:Intent intent = new Intent(); //不設定action值 //intent.setAction("com.ispring.action.ACTION_TEST1"); Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun"); intent.setData(uri);
上面的intent物件可以通過如下的intent-filter:
<intent-filter> <action android:name="com.csdn.action.ACTION_XXX" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> </intent-filter>
上面的intent物件無法通過如下的intent-filter:
<intent-filter> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> </intent-filter>
通過上面的幾個示例,想必大家都已經理解了action測試的規則,至於上面的category和data標籤的使用,會在下面詳細介紹。
總結起來有兩點結論:
1. 要想讓intent物件通過action測試,那麼intent-filter中宣告的action不能為空且要包含intent物件中的action值(如果intent的action值不為空的話)。
2. 如果intent-filter沒有宣告任何action,那麼所有的intent的物件(即無論intent如何配置)都無法通過intent-filter的action測試。
Category測試
為了指定能夠接收並處理的Intent的型別,元件可以在intent-filter中宣告其支援0個或多個category,例如:
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
...
</intent-filter>
intent物件有addCategory()方法,也就是說一個intent物件也可以關聯多個category。為了能讓intent物件通過intent-filter的category測試,intent物件中的所有category都要在intent-filter中找到對應項。
具體來說,又分為如下兩種情況:
intent物件至少有一個category
這種情況下,假設intent物件有N個category(N >=1),那麼intent-filter中必須要包含這N個category,intent物件才能通過category測試,否則無法通過測試。如果用intent物件啟動Activity,還有其他限制條件,會在後面詳細說明。
舉個例子,假設我們的intent-filter如下所示:<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="com.ispring.category.TEST1" /> <category android:name="com.ispring.category.TEST2" /> </intent-filter>
以下intent物件能夠通過category測試
Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1"); intent.addCategory("com.ispring.category.TEST1"); intent.addCategory("com.ispring.category.TEST2");
該intent物件之所以可以通過category測試是因為intent-filter包含了該intent對中所有的category值:com.ispring.category.TEST1”和com.ispring.category.TEST2。
以下intent物件無法通過category測試
Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1"); intent.addCategory("com.ispring.category.TEST1"); intent.addCategory("com.ispring.category.TEST3");
該intent之所以無法通過上面的intent-filter的category測試是因為intent-filter只包含了該intent中值為com.ispring.category.TEST1的category,而並未包含值為com.ispring.category.TEST3的category,不滿足完全包含intent中全部category的情況。
intent物件不包含任何category
如果intent物件沒有呼叫過addCategory()方法,那麼intent物件就不包含任何的category。這種情形下,如果該intent不是用來啟動Activity的話,那麼無論intent-filter中category中如何配置,intent物件總是能通過intent-filter的category測試,即便intent-filter中沒有宣告任何的category,intent都能通過category測試。此處強調了該intent不是用來啟動Activity這種條件,會在下面詳細解釋。
此處需要特別說明的是,我們在上面所有的示例中,都給Activity的intent-filter新增了值為android.intent.category.DEFAULT
的category,這是因為當我們把一個隱式的intent傳遞給startActivity()
或startActivityForResult()
方法時,Android會自動給該隱式intent新增值為android.intent.category.DEFAULT
的category,所以為了能讓intent-filter包含intent中全部的category,我們就需要在Activity的intent-filter中新增該category,在使用時需要特別注意。
根據上面我們的幾個示例,我們總結如下:
1. 如果intent物件不包含任何category,並且該intent不是用來啟動Activity的,那麼該intent物件總是能通過所有任意的intent-filter的category測試;
2. 如果intent物件包含category(至少一個),那麼只有當intent-filter中宣告的category全部包含intent物件中的所有category的時候才通過category測試。
3. 如果允許Activity被隱式的Intent啟動,那麼我們必須在該Activity的intent-filter中宣告值為android.intent.category.DEFAULT
的category。
Data測試
為了指定可以接收的Intent的data,intent-filter需要宣告0個多多個<data />
標籤,例如:
<intent-filter>
<data android:mimeType="video/mpeg" android:scheme="http" ... />
<data android:mimeType="audio/mpeg" android:scheme="http" ... />
...
</intent-filter>
每個<data />
標籤都可以指定一個URI結構以及data的MIME型別。一個完整的URI由scheme
、host
、port
和path
組成,其結構如下所示:
<scheme>://<host>:<port>/<path>
其中scheme既可以是Android中常見的協議,也可以是我們自定義的協議。Android中常見的協議包括content協議、http協議、file協議等,自定義協議可以使用自定義的字串。
如下是一個content協議的URI:
content://com.example.project:200/folder/subfolder/etc
在該URI中,scheme是content,host是com.example.project,port是200,path是folder/subfolder/etc。如下是一個自定義協議的URI:
ispring://blog.csdn.net/sunqunsunqun
在該URI中,scheme是ispring,host是blog.csdn.net,沒有明確設定port,path是sunqunsunqun。
組成URI的這些屬性在<data />
標籤中都是可選的 ,但存在如下的依賴關係:
- 如果沒有指定scheme,那麼host引數會被忽略
- 如果沒有指定host,那麼port引數會被忽略
- 如果scheme和host都沒有指定,path引數會被忽略
當我們將intent物件中的Uri引數與intent-filter中的<data />
標籤指定的URI格式進行對別時,我們我們只對比intent-filter的<data />
標籤指定的部分,例如:
- 如果intent-filter中只指定了scheme,那麼所有帶有該sheme的URI都能匹配到該intent-filter。
- 如果intent-filter中只指定了scheme和authority(authority包括host和port兩部分)而沒有指定path,那麼所有具有相同scheme和authority的URI都能匹配到該intent-filter,而不用考慮path為何值。
- 如果intent-filter中同時指定了scheme、authority和path,那麼只有具有相同scheme、authority和path的URI才能匹配到該intent-filter。
需要注意的是,intent-filter的<data />
標籤在指定path的值時,可以在裡面使用萬用字元*
,起到部分匹配的效果。
data測試需要同時將intent物件中的URI、MIME型別與intent-filter的<data />
標籤中指定的URI、MIME型別進行對比。
我們知道一個intent-filter下可以有多個<data />
標籤,intent物件無需通過所有的<data />
標籤測試,一般情況下,我們的intent物件只需通過了其中一個<data />
標籤的測試並滿足某些特定情形下的一些條件,那麼該intent物件就通過了該intent-filter的data測試。
進行對比的規則分以下幾種情況:
intent物件不包含URI和MIME型別
這種情況下,只有當intent-filter也沒有指定任何URI和MIME型別的時候才能通過data測試。
例如我們有如下intent物件:Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1");
上面的intent物件可以通過下面的intent-filter的data測試:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter>
上面的intent物件無法通過下面的intent-filter測試:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" /> </intent-filter>
intent物件包含URI但不包含MIME型別
這種情況下,只有當intent物件的URI匹配到了intent-filter中的URI格式,並且intent-filter沒有指定MIME型別的時候才能通過data測試。需要注意的是,這裡所說的intent-filter沒有指定MIME型別的情形指的是intent-filter中所有的<data />
標籤都沒有指定MIME型別,即整個intent-filter中完全沒有android:mimeType
這幾個字,理解這點很重要,大家在下面的幾個示例中可以體會到這點。
例如有如下intent物件:Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1"); Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun"); intent.setData(uri);
上面的intent能通過如下的intent-filter的data測試:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> </intent-filter>
上面的intent物件可以通過以下intent-filter的data測試:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> <data android:scheme="sunqun" android:host="8080" /> </intent-filter>
intent物件雖然不能通過scheme為sunqun的
<data />
標籤測試,但是可以通過scheme為ispring的data標籤測試,且intent物件和intent-filter中的兩個<data />
標籤都沒有指定MIME,所以上面的intent物件可以通過該intent-filter測試。上面的intent物件無法通過以下intent-filter的
<data />
標籤測試:<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" android:scheme="ispring" android:host="blog.csdn.net" /> </intent-filter>
上面的intent物件之所以不能通過intent-filter中唯一的一個
<data />
標籤測試是因為我們的intent物件沒有指定MIME型別,但是上面的<data />
標籤通過android:mimeType="text/plain"
設定了MIME型別。上面的intent物件無法通過以下intent-filter的data測試:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> <data android:mimeType="text/plain" /> </intent-filter>
上面的intent物件之所以無法通過該intent-filter中的data測試,是因為intent物件沒有設定MIME型別,但是intent-filter中第二個data標籤通過
android:mimeType="text/plain"
設定了MIME型別。
intent物件包含MIME型別但不包含URI
這種情況下,只有當intent中的MIME型別與intent-filter中列出的MIME型別相同,並且intent-filter沒有指定任何的URI格式的時候才能通過data測試。需要注意的是,這裡所說的intent-filter沒有指定任何的URI格式的情形指的是intent-filter中所有<data />
標籤都沒有指定URI,即整個intent-filter中完全沒有android:scheme
、android:host
、android:port
以及android:path
,理解這點很重要,大家在下面的幾個示例中可以體會到這點。
例如有如下intent物件:Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1"); intent.setType("text/plain");
上面的intent物件可以通過以下intent-filter的data測試:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter>
上面的intent物件可以通過下面的intent-filter的data測試:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="image/*" /> <data android:mimeType="text/plain" /> </intent-filter>
上面的intent物件雖然沒有通過MIME型別為
image/*
的第一個data標籤測試,但能通過第二個data標籤測試,並且intent物件和intent-filter都沒有指定任何的URI格式。上面的intent物件不能通過以下intent-filter中的data測試:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" android:scheme="ispring" /> </intent-filter>
上面的intent物件中沒有設定URI資訊,但是在該intent-filter中設定了URI中的scheme值,所以intent無法通過intent-filter的data測試。
上面的intent物件無法通過以下intent-filter中的data測試:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> <data android:scheme="ispring" /> </intent-filter>
上面的intent物件沒有指定URI資訊,但是上面的intent-filter中第二個
<data />
標籤設定了URI中的scheme資訊,所以intent物件無法通過該intent-filter的data測試。
intent物件同時包含URI和MIME型別
這種情況下,要分別測試URI以及MIME型別測試是否通過,只有URI以及MIME測試都通過了,data測試才能通過。- 對於MIME測試:如果intent的MIME型別能夠匹配intent-filter中列出的某一個
<data />
標籤中的MIME型別值,那麼MIME型別測試就通過了。 - 對於URI測試:
又細分兩種情況,滿足下面的任何一種情況都可以通過URI測試。
- 如果intent的URI格式能夠匹配intent-filter中列出的某一個
<data />
中的URI,那麼URI測試就通過了。 - 如果intent的URI是
content:
協議或file:
協議,並且整個intent-filter的所有<data />
標籤中都沒有指定URI,那麼該intent也能通過URI測試。換句話說,如果一個intent-filter只列出了MIME型別,沒有列出任何URI相關的格式的話,那麼這個intent-filter就預設是支援content:
協議或file:
協議的。
- 如果intent的URI格式能夠匹配intent-filter中列出的某一個
下面舉幾個例子大家自己體會一下。
假設有如下協議為自定義協議
ispring:
的intent物件:Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1"); Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun"); String type = "text/plain"; intent.setDataAndType(uri, type);
上面的intent物件可以通過下面的intent-filter的data測試:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> <data android:mimeType="text/plain" /> </intent-filter>
上面的intent物件無法通過下面的intent-filter的data測試:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" android:port="8080" /> <data android:mimeType="text/plain" /> </intent-filter>
port不滿足,URI測試不通過,導致data測試失敗。
上面的intent物件無法通過下面的intent-filter的data測試:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> <data android:mimeType="image/*" /> </intent-filter>
android:mimeType
不滿足,MIME型別測試不通過,導致data測試失敗。
假設有如下協議為
content:
的intent物件:Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1"); Uri uri = Uri.parse("content://com.ispring.test"); String type = "text/plain"; intent.setDataAndType(uri, type);
上面的intent物件無法通過下面的intent-filter的data測試:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" /> <data android:mimeType="text/plain" /> </intent-filter>
URI中的scheme不匹配,導致URI測試不通過,導致data測試失敗。
上面的intent物件可以通過下面的intent-filter的data測試:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter>
intent中使用的是
content:
協議,並且整個intent-filter中都沒有定義URI格式,所以URI測試是可以通過的,並且MIME型別能找到匹配項,所以可以通過data測試。
- 對於MIME測試:如果intent的MIME型別能夠匹配intent-filter中列出的某一個
綜上,我們就完成了對Intent中action、category、data測試的詳細解釋,本文所有示例程式碼均在Android Studio 1.0正式版中驗證過沒有問題。很感謝大家能夠耐心讀完本博文,希望本文對大家正確使用Intent過濾器有所幫助。
如果有疑問,歡迎大家在評論中給我留言,看到會及時回覆。
相關文章
- Intent詳解(二)----Intent過濾器Intent過濾器
- Android——Intent和Intent過濾器AndroidIntent過濾器
- Android中的Intent Filter匹配規則介紹AndroidIntentFilter
- Android 通過 Intent 傳遞類物件AndroidIntent物件
- Java 中的 Filter 過濾器詳解JavaFilter過濾器
- 你必須弄懂的Intent Filter匹配規則IntentFilter
- Filter(過濾器)與Listener(監聽器)詳解Filter過濾器
- Android中的intentAndroidIntent
- HBase Filter 過濾器之 ValueFilter 詳解Filter過濾器
- 布隆過濾器(Bloom Filter)詳解過濾器OOMFilter
- Activity配置檔案中的intent-filterIntentFilter
- Android應用開發—Intent元件詳解AndroidIntent元件
- Android intent傳遞list或物件AndroidIntent物件
- JavaWeb 中 Filter過濾器JavaWebFilter過濾器
- filter過濾Filter
- Android中Intent概述及使用AndroidIntent
- Android Intent ServiceAndroidIntent
- 使用Intent傳遞物件Intent物件
- java中listFiles(Filefilter filter)檔案過濾器的實現過程JavaFilter過濾器
- Filter過濾器Filter過濾器
- Android Intent Action 大全AndroidIntent
- 4Intent物件簡介Intent物件
- PHP 過濾器(Filter)PHP過濾器Filter
- Java Filter過濾器JavaFilter過濾器
- lucene Filter過濾器Filter過濾器
- IntentIntent
- 攔截器(Interceptor)與過濾器(Filter)過濾器Filter
- filter在JavaScript中過濾陣列元素FilterJavaScript陣列
- Android StartActivies(Intent[] intents)用法AndroidIntent
- android-Activity Intent.setFlags()與launchModeAndroidIntent
- Elasticsearch——filter過濾查詢ElasticsearchFilter
- Filter過濾器的使用Filter過濾器
- JavaWeb - 【Filter】敏感詞過濾JavaWebFilter
- 最全面的Android Intent機制講解AndroidIntent
- sticky INTENTIntent
- Intent 解析Intent
- Android Intent Flag組合使用AndroidIntent
- android廣播集合,intent,actionAndroidIntent