Intent和他的過濾器

什么是Intent

intent是一個消息傳遞對象,可以使用它從其他應(yīng)用組件請求操作。通常我們用intent來啟動一個activity,服務(wù)或者是廣播,

啟動activity

? ? 使用startactivity() 來啟動一個activity。intent則封裝了要啟動的activity的信息以及傳遞的數(shù)據(jù)。如果你希望在activity完成后收到結(jié)果,你需要調(diào)用startactivityforresult()方法,在activity的onactivityresult()回調(diào)中的intent返回了數(shù)據(jù)內(nèi)容

啟動服務(wù)

? ? service是一個后臺執(zhí)行操作的組件,他沒有用戶界面,通過startservice打開一個服務(wù)

傳遞廣播

? ? 廣播是任何應(yīng)用都可以接受的消息,通過sendbroadcast()傳遞廣播

就拿發(fā)送email舉例吧,發(fā)送email必要條件:發(fā)送者,接受者,和載體(就是email本身)。intent類似于email,他作為一個傳遞對象封裝了要傳遞的信息和內(nèi)容,再由發(fā)送者(調(diào)用startactivity的界面)發(fā)送給接收者。



?Intent 過濾器

? ? intent過濾器是應(yīng)用的清單文件中的一個表達式,他制定該組件要接收的intent類型,比如:通過給activity聲明intent過濾器,可以使其他應(yīng)用能夠直接使用某一特定類型的intent來啟動activity,如果沒有給activity聲明過濾器就不能通過隱式意圖來啟動,只能通過顯式意圖來啟動activity


Intent的類型

intent的類型的類型有兩種,一種叫顯式意圖,一種叫隱式意圖

顯式意圖:通常在自己的程序中使用,明確知道我們要做什么事情,啟動什么組件。

隱式意圖:通常是調(diào)用第三方程序執(zhí)行,我們不知道組件的名稱,但是知道要執(zhí)行的操作,制定匹配規(guī)則,讓滿足規(guī)則的程序來執(zhí)行。

創(chuàng)建隱式意圖時,android系統(tǒng)通過將intent的內(nèi)容與設(shè)備上其他應(yīng)用清單文件聲明的intent過濾器進行比較,從而找到要啟動的對應(yīng)的組件,如果intent與ent過濾器匹配,則系統(tǒng)降啟動這個組件,同事傳遞intent對象給組件, 如果有多個intent過濾器相匹配則系統(tǒng)會顯示一個列表對話框,顯示所有匹配的組件供用戶自行選擇要使用的應(yīng)用。

創(chuàng)建一個顯式意圖

Intent intent =new Intent();? 創(chuàng)建意圖對象

intent.setClass(c, ScanActivity.class);? 設(shè)置要打開的目標(biāo)

intent.putExtra("title", title);? 給意圖填充數(shù)據(jù)

intent.putExtra("hint", hint);

intent.putExtra("typeName", typeName);

activity.startActivity(intent);? 開啟

創(chuàng)建一個隱式意圖intent

創(chuàng)建隱式意圖要注意的是,如果手機沒有任何應(yīng)用來處理你的隱式意圖,則調(diào)用失敗應(yīng)用會崩掉,所以在這之前要調(diào)用intent.resolveActivity()方法判斷是否有應(yīng)用匹配規(guī)則,如果返回非空,則至少有一個應(yīng)用能夠處理該意圖,則可以調(diào)用,否則不應(yīng)該調(diào)用

Intent sendIntent = new Intent();

sendIntent.setAction(Intent.ACTION_SEND); 表明你要做什么事情

sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage); 目標(biāo)需要的內(nèi)容

sendIntent.setType("text/plain"); 數(shù)據(jù)類型

if (sendIntent.resolveActivity(getPackageManager()) != null) {

startActivity(sendIntent);}

接收隱式意圖

? ? 要想應(yīng)用能夠接收隱式意圖,必須在清單文件中使用<intent-filter>元素為組件聲明過濾器。<intent-filter>元素應(yīng)該嵌入對應(yīng)的組件中,通常為activity。我們可以為組件設(shè)置一個或者多個過濾器,每個過濾器均需要根據(jù)意圖操作,數(shù)據(jù)和類別指定自身接收的類型,當(dāng)隱式意圖滿足過濾器規(guī)則時,系統(tǒng)就會將該意圖傳遞給應(yīng)用組件。

<intent-filter>元素的子元素

? ? <action>

在那么屬性中,聲明接受的意圖操作,鈣質(zhì)必須為操作的文本字符串值,而不是常量類。

<action android:name="android.intent.action.SEND"> name屬性的值可以自定義,也可以使用安卓系統(tǒng)提供的一些默認(rèn)值

? ? <data>

使用一個或者多個指定數(shù)據(jù)URI各個方面(scheme,host,port,path等)和MIME類型的屬性,聲明接受的數(shù)據(jù)類型

<data android:mimeType="text/plain"/>

每個data分為MIME?和scheme。scheme包含host,port,path等。

MIME (Multipurpose?Internet?Mail?Extensions) 是描述消息內(nèi)容類型的因特網(wǎng)標(biāo)準(zhǔn)。

? ? <category>

在name屬性中,聲明接受的意圖類別,鈣質(zhì)必須是操作文本字符串值,而不是常量類。

<category android:name="android.intent.category.DEFAULT>

為了接受隱式意圖,必須將 category.DEFAULT 類別包括在過濾器中,如果沒有在過濾器聲明此類別,則隱式意圖不會解析該組件


Intent Filter匹配規(guī)則

Intent解析機制主要是通過查找已注冊在AndroidManifest.xml中的所有IntentFilter及其中定義的Intent,最終找到匹配的Intent。在這個解析過程中,Android是通過Intent的action、type、category這三個屬性來進行匹配判斷的。一個過濾列表中的action、type、category可以有多個,所有的action、type、category分別構(gòu)成不同類別,同一類別信息共同約束當(dāng)前類別的匹配過程。只有一個Intent同時匹配action、type、category這三個類別才算完全匹配,只有完全匹配才能啟動Activity。另外一個組件若聲明了多個Intent Filter,只需要匹配任意一個即可啟動該組件。?

(1)action的匹配規(guī)則

? ? ? ?action是一個字符串,如果Intent指明定了action,則目標(biāo)組件的IntentFilter的action列表中就必須包含有這個action,否則不能匹配。一個Intent Filter中可聲明多個action,Intent中的action與其中的任一個action在字符串形式上完全相同(注意,區(qū)分大小寫,大小寫不同但字符串內(nèi)容相同也會造成匹配失?。?,action方面就匹配成功。可通過setAction方法為Intent設(shè)置action,也可在構(gòu)造Intent時傳入action。需要注意的是,隱式Intent必須指定action。比如我們在Manifest文件中為MyActivity定義了如下Intent Filter:

<intent-filter>

? ? ? <action android:name="android.intent.action.SEND"/>

????<action android:name="android.intent.action.SEND_TO"/>

</intent-filter>

? ? ? ?那么只要Intent的action為“SEND”或“SEND_TO”,那么這個Intent在action方面就能和上面那個Activity匹配成功。比如我們的Intent定義如下:

Intent intent =newIntent("android.intent.action.SEND") ;startActivity(intent);

那么我們的Intent在action方面就與MyActivity匹配了。?

Android系統(tǒng)預(yù)定義了許多action,這些action代表了一些常見的操作。常見action如下(Intent類中的常量):

Intent.ACTION_VIEW

Intent.ACTION_DIAL

Intent.ACTION_SENDTO

Intent.ACTION_SEND

Intent.ACTION_WEB_SEARCH

總結(jié)起來有兩點結(jié)論:?

1. 要想讓intent對象通過action測試,那么intent-filter中聲明的action不能為空且要包含intent對象中的action值(如果intent的action值不為空的話)。?

2. 如果intent-filter沒有聲明任何action,那么所有的intent的對象(即無論intent如何配置)都無法通過intent-filter的action測試。

(2)data的匹配規(guī)則

如果Intent沒有提供type,系統(tǒng)將從data中得到數(shù)據(jù)類型。和action一樣,同action類似,只要Intent的data只要與Intent Filter中的任一個data聲明完全相同,data方面就完全匹配成功。?

data由兩部分組成:mimeType和URI?

MineType指的是媒體類型:例如imgage/jpeg,auto/mpeg4和viedo/*等,可以表示圖片、文本、視頻等不同的媒體格式?

uri則由scheme、host、port、path | pathPattern | pathPrefix這4部分組成

其中scheme既可以是Android中常見的協(xié)議,也可以是我們自定義的協(xié)議。Android中常見的協(xié)議包括content協(xié)議、http協(xié)議、file協(xié)議等,自定義協(xié)議可以使用自定義的字符串。

如下是一個content協(xié)議的URI:

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

在該URI中,scheme是content,host是com.example.project,port是200,path是folder/subfolder/etc。

如下是一個自定義協(xié)議的URI:

ispring://blog.csdn.net/sunqunsunqun?

在該URI中,scheme是ispring,host是blog.csdn.net,沒有明確設(shè)定port,path是sunqunsunqun。

組成URI的這些屬性在標(biāo)簽中都是可選的 ,但存在如下的依賴關(guān)系:

如果沒有指定scheme,那么host參數(shù)會被忽略

如果沒有指定host,那么port參數(shù)會被忽略

如果scheme和host都沒有指定,path參數(shù)會被忽略

當(dāng)我們將intent對象中的Uri參數(shù)與intent-filter中的標(biāo)簽指定的URI格式進行對別時,我們我們只對比intent-filter的標(biāo)簽指定的部分,例如:

如果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的標(biāo)簽在指定path的值時,可以在里面使用通配符*,起到部分匹配的效果。

data測試需要同時將intent對象中的URI、MIME類型與intent-filter的標(biāo)簽中指定的URI、MIME類型進行對比。?

我們知道一個intent-filter下可以有多個標(biāo)簽,intent對象無需通過所有的標(biāo)簽測試,一般情況下,我們的intent對象只需通過了其中一個標(biāo)簽的測試并滿足某些特定情形下的一些條件,那么該intent對象就通過了該intent-filter的data測試。?

Intent的uri可通過setData方法設(shè)置,mimetype可通過setType方法設(shè)置。?

需要注意的是:若Intent Filter的data聲明部分未指定uri,則缺省uri為content或file,Intent中的uri的scheme部分需為content或file才能匹配;若要為Intent指定完整的data,必須用setDataAndType方法,究其原因在,setData和setType方法的源碼中我們發(fā)現(xiàn):

publicIntentsetData(Uri data) {?

?? mData = data;?

?? mType =null;

????returnthis;

}

publicIntent setType(Stringtype) {

mData =null;? ?

?mType =type;

returnthis;

}

這兩個方法會彼此互相清除對方的值(這個比較逗),即setData會把mimeType置為null,setType會把uri置為null。?

下面我們來舉例說明一下data的匹配。首先我們先來看一下Intent Filter中指定data的語法:

<data android:scheme="String.“

? ? ? ? ? android:host="String"

? ? ? ? ? android:port="String"

? ? ? ? ? android:path="String"

? ? ? ? ? android:pathPattern="String"

? ? ? ? ? android:pathPrefix="String"

? ? ? ? ? android:mimeType="String"/>

? ? 其中scheme、host等各個部分無需全部指定。


使用案例:?

(1)如果我們想要匹配 http 以 “.pdf” 結(jié)尾的路徑,使得別的程序想要打開網(wǎng)絡(luò) pdf 時,用戶能夠可以選擇我們的程序進行下載查看。?

我們可以將 scheme 設(shè)置為 “http”,pathPattern 設(shè)置為 “.*//.pdf”,整個 intent-filter 設(shè)置為:

<intent-filter>

<actionandroid:name="android.intent.action.VIEW">

<categoryandroid:name="android.intent.category.DEFAULT">

<dataandroid:scheme="http"android:pathPattern=".*//.pdf">

</intent-filter>

如果你只想處理某個站點的 pdf,那么在 data 標(biāo)簽里增加 android:host=”yoursite.com” 則只會匹配?http://yoursite.com/xxx/xxx.pdf,但這不會匹配 www.yoursite.com,如果你也想匹配這個站點的話,你就需要再添加一個 data 標(biāo)簽,除了 android:host 改為 “www.yoursite.com” 其他都一樣。

(2)如果我們做的是一個IM應(yīng)用,或是其他類似于微博之類的應(yīng)用,如何讓別人通過 Intent 進行調(diào)用出現(xiàn)在選擇框里呢?我們只用注冊 android.intent.action.SEND 與 mimeType 為 “text/plain” 或 “/” 就可以了,整個 intent-filter 設(shè)置為:

<intent-filter>

<actionandroid:name="android.intent.action.SEND"/>

<categoryandroid:name="android.intent.category.DEFAULT"/>

<datamimeType="*/*"/>

</intent-filter>

這里設(shè)置 category 的原因是,創(chuàng)建的 Intent 的實例默認(rèn) category 就包含了 Intent.CATEGORY_DEFAULT ,google 這樣做的原因是為了讓這個 Intent 始終有一個 category。

(3)如果我們做的是一個音樂播放軟件,當(dāng)文件瀏覽器打開某音樂文件的時候,使我們的應(yīng)用能夠出現(xiàn)在選擇框里?這類似于文件關(guān)聯(lián)了,其實做起來跟上面一樣,也很簡單,我們只用注冊 android.intent.action.VIEW 與 mimeType 為 “audio/*” 就可以了,整個 intent-filter 設(shè)置為:

<intent-filter>

<actionandroid:name="android.intent.action.VIEW"/>

<categoryandroid:name="android.intent.category.DEFAULT"/>

<dataandroid:mimeType="audio/*"/>

</intent-filter>

(3)category的匹配規(guī)則

? ? ? ?category也是一個字符串,但是它與action的過濾規(guī)則不同,它要求Intent中個如果含有category,那么所有的category都必須和過濾規(guī)則中的其中一個category相同。也就是說,Intent中如果出現(xiàn)了category,不管有幾個category,對于每個category來說,它必須是過濾規(guī)則中的定義了的category。當(dāng)然,Intent中也可以沒有category(若Intent中未指定category,系統(tǒng)會自動為它帶上“android.intent.category.DEFAULT”),如果沒有,仍然可以匹配成功。category和action的區(qū)別在于,action要求Intent中必須有一個action且必須和過濾規(guī)則中的某幾個action相同,而category要求Intent可以沒有category,但是一旦發(fā)現(xiàn)存在category,不論你有多少,每個都要能夠和過濾規(guī)則中的任何一個category相同。我們可以通過addCategory方法為Intent添加category。

1. 如果intent對象不包含任何category,并且該intent不是用來啟動Activity的,那么該intent對象總是能通過所有任意的intent-filter的category測試;?

2. 如果intent對象包含category(至少一個),那么只有當(dāng)intent-filter中聲明的category全部包含intent對象中的所有category的時候才通過category測試。?

3. 如果允許Activity被隱式的Intent啟動,那么我們必須在該Activity的intent-filter中聲明值為android.intent.category.DEFAULT的category。

特別說明:

<intent-filter>

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

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

</intent-filter>

這二者共同出現(xiàn),標(biāo)明該Activity是一個入口Activity,并且會出現(xiàn)在系統(tǒng)應(yīng)用列表中,二者缺一不可。

Intent Filter常見問題匯總

(1)path、pathPrefix、pathPattern 之間的區(qū)別

path 用來匹配完整的路徑,如:http://example.com/blog/abc.html,這里將 path 設(shè)置為 /blog/abc.html 才能夠進行匹配;?

pathPrefix 用來匹配路徑的開頭部分,拿上來的 Uri 來說,這里將 pathPrefix 設(shè)置為 /blog 就能進行匹配了;?

pathPattern 用表達式來匹配整個路徑,這里需要說下匹配符號與轉(zhuǎn)義。?

匹配符號:?

” 用來匹配0次或更多,如:“a” 可以匹配“a”、“aa”、“aaa”…?

“.” 用來匹配任意字符,如:“.” 可以匹配“a”、“b”,“c”…?

因此 “.*” 就是用來匹配任意字符0次或更多,如:“.*html” 可以匹配 “abchtml”、“chtml”,“html”,“sdf.html”…?

轉(zhuǎn)義:因為當(dāng)讀取 Xml 的時候,“/” 是被當(dāng)作轉(zhuǎn)義字符的(當(dāng)它被用作 pathPattern 轉(zhuǎn)義之前),因此這里需要兩次轉(zhuǎn)義,讀取 Xml 是一次,在 pathPattern 中使用又是一次。如:“” 這個字符就應(yīng)該寫成 “//”,“/” 這個字符就應(yīng)該寫成 “////”。

(2)查詢是否有Activity可以匹配我們指定Intent的組件

采用PackageManager的resolveActivity或者Intent的resolveActivity方法會獲得最適合Intent的一個Activity?

調(diào)用PackageManager的queryIntentActivities會返回所有成功匹配Intent的Activity

(3)android.intent.action.MAIN 與android.intent.category.LAUNCHER的區(qū)別

區(qū)別一:?

android.intent.action.MAIN決定一個應(yīng)用程序最先啟動那個組件?

android.intent.category.LAUNCHER決定應(yīng)用程序是否顯示在程序列表里(說白了就是是否在桌面上顯示一個圖標(biāo))?

這兩個屬性組合情況:?

第一種情況:有MAIN,無LAUNCHER,程序列表中無圖標(biāo)?

原因:android.intent.category.LAUNCHER決定應(yīng)用程序是否顯示在程序列表里?

第二種情況:無MAIN,有LAUNCHER,程序列表中無圖標(biāo)?

原因:android.intent.action.MAIN決定應(yīng)用程序最先啟動的Activity,如果沒有Main,則不知啟動哪個Activity,故也不會有圖標(biāo)出現(xiàn)?

所以這兩個屬性一般成對出現(xiàn)。?

如果一個應(yīng)用中有兩個組件intent-filter都添加了android.intent.action.MAIN和?

android.intent.category.LAUNCHER這兩個屬性, 則這個應(yīng)用將會顯示兩個圖標(biāo), 寫在前面的組件先運行。?

區(qū)別二:

android.intent.category.LAUNCHER:android.intent.category.LAUNCHER決定應(yīng)用程序是否顯示在程序列表里,就是android開機后的主程序列表。?

android.intent.category.HOME:按住“HOME”鍵,該程序顯示在HOME列表里。

(4)關(guān)于隱式intent

每一個通過 startActivity() 方法發(fā)出的隱式 Intent 都至少有一個 category,就是 “android.intent.category.DEFAULT”,所以只要是想接收一個隱式 Intent 的 Activity 都應(yīng)該包括 “android.intent.category.DEFAULT” category,不然將導(dǎo)致 Intent 匹配失敗.?


(5)于intent-filter匹配優(yōu)先級

首先查看Intent的過濾器(intent-filter),按照以下優(yōu)先關(guān)系查找:action->data->category

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容