Intent簡(jiǎn)介
Android中提供了Intent機(jī)制來協(xié)助應(yīng)用間的交互與通訊,Intent負(fù)責(zé)對(duì)應(yīng)用中一次操作的動(dòng)作、動(dòng)作涉及數(shù)據(jù)、附加數(shù)據(jù)進(jìn)行描述,Android則根據(jù)此Intent的描述,負(fù)責(zé)找到對(duì)應(yīng)的組件,將 Intent傳遞給調(diào)用的組件,并完成組件的調(diào)用。Intent不僅可用于應(yīng)用程序之間,也可用于應(yīng)用程序內(nèi)部的Activity/Service之間的交互。因此,Intent在這里起著一個(gè)媒體中介的作用,專門提供組件互相調(diào)用的相關(guān)信息,實(shí)現(xiàn)調(diào)用者與被調(diào)用者之間的解耦。在SDK中給出了Intent作用的表現(xiàn)形式為:
通過Context.startActivity() orActivity.startActivityForResult() 啟動(dòng)一個(gè)Activity;
通過 Context.startService() 啟動(dòng)一個(gè)服務(wù),或者通過Context.bindService() 和后臺(tái)服務(wù)交互;
通過廣播方法(比如 Context.sendBroadcast(),Context.sendOrderedBroadcast(), Context.sendStickyBroadcast()) 發(fā)給broadcast receivers。
Intent可分為隱式(implicitly)和顯式(explicitly)兩種:
(1)顯示 Intent
即在構(gòu)造Intent對(duì)象時(shí)就指定接收者,它一般用在知道目標(biāo)組件名稱的前提下,一般是在相同的應(yīng)用程序內(nèi)部實(shí)現(xiàn)的,如下:
Intent intent = new Intent(MainActivit.this, NewActivity.class);startActivity(intent );
上面那個(gè)intent中,直接指明了接收者:NewActivity
(2)隱式 Intent
即Intent的發(fā)送者在構(gòu)造Intent對(duì)象時(shí),并不知道也不關(guān)心接收者是誰,有利于降低發(fā)送者和接收者之間的耦合,它一般用在沒有明確指出目標(biāo)組件名稱的前提下,一般是用于在不同應(yīng)用程序之間,如下:
Intent intent = new Intent();intent.setAction("com.wooyun.test");startActivity(intent);
上面那個(gè)intent,沒有指明接收者,只是給了一個(gè)action作為接收者的過濾條件。 對(duì)于顯式Intent,Android不需要去做解析,因?yàn)槟繕?biāo)組件已經(jīng)很明確,Android需要解析的是那些隱式Intent,通過解析,將Intent映射給可以處理此Intent的Activity、IntentReceiver或Service。
Intent Filter匹配規(guī)則
Intent解析機(jī)制主要是通過查找已注冊(cè)在AndroidManifest.xml中的所有IntentFilter及其中定義的Intent,最終找到匹配的Intent。在這個(gè)解析過程中,Android是通過Intent的action、type、category這三個(gè)屬性來進(jìn)行匹配判斷的。一個(gè)過濾列表中的action、type、category可以有多個(gè),所有的action、type、category分別構(gòu)成不同類別,同一類別信息共同約束當(dāng)前類別的匹配過程。只有一個(gè)Intent同時(shí)匹配action、type、category這三個(gè)類別才算完全匹配,只有完全匹配才能啟動(dòng)Activity。另外一個(gè)組件若聲明了多個(gè)Intent Filter,只需要匹配任意一個(gè)即可啟動(dòng)該組件。 例如:
<action android:name="com.wooyun.project.SHOW_CURRENT" /><category android:name="android.intent.category.DEFAULT" /><data android:mimeType="video/mpeg" android:scheme="http" . . . /><data android:mimeType="image/*" /><data android:scheme="http" android:type="video/*" />
(1)action的匹配規(guī)則
action是一個(gè)字符串,如果Intent指明定了action,則目標(biāo)組件的IntentFilter的action列表中就必須包含有這個(gè)action,否則不能匹配。一個(gè)Intent Filter中可聲明多個(gè)action,Intent中的action與其中的任一個(gè)action在字符串形式上完全相同(注意,區(qū)分大小寫,大小寫不同但字符串內(nèi)容相同也會(huì)造成匹配失?。琣ction方面就匹配成功。可通過setAction方法為Intent設(shè)置action,也可在構(gòu)造Intent時(shí)傳入action。需要注意的是,隱式Intent必須指定action。比如我們?cè)贛anifest文件中為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”,那么這個(gè)Intent在action方面就能和上面那個(gè)Activity匹配成功。比如我們的Intent定義如下:
Intent intent = new Intent("android.intent.action.SEND") ;startActivity(intent);
那么我們的Intent在action方面就與MyActivity匹配了。 Android系統(tǒng)預(yù)定義了許多action,這些action代表了一些常見的操作。常見action如下(Intent類中的常量):
Intent.ACTION_VIEWIntent.ACTION_DIALIntent.ACTION_SENDTOIntent.ACTION_SENDIntent.ACTION_WEB_SEARCH
(2)data的匹配規(guī)則
如果Intent沒有提供type,系統(tǒng)將從data中得到數(shù)據(jù)類型。和action一樣,同action類似,只要Intent的data只要與Intent Filter中的任一個(gè)data聲明完全相同,data方面就完全匹配成功。 data由兩部分組成:mimeType和URI MineType指的是媒體類型:例如imgage/jpeg,auto/mpeg4和viedo/*等,可以表示圖片、文本、視頻等不同的媒體格式 uri則由scheme、host、port、path | pathPattern | pathPrefix這4部分組成
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern >]
例如: content://com.wooyun.org:200/folder/etc http://www.wooyun.org:80/search/info
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):
public Intent setData(Uri data)
{
mData = data; mType = null; return this;
}
public Intent setType(String type) { mData = null; mType = type; return this;}
這兩個(gè)方法會(huì)彼此互相清除對(duì)方的值(這個(gè)比較逗),即setData會(huì)把mimeType置為null,setType會(huì)把uri置為null。 下面我們來舉例說明一下data的匹配。首先我們先來看一下Intent Filter中指定data的語(yǔ)法:
<data android:scheme="String.“ android:host="String" android:port="String" android:path="String" android:pathPattern="String" android:pathPrefix="String" android:mimeType="String"/> 其中scheme、host等各個(gè)部分無需全部指定。
使用案例: (1)如果我們想要匹配 http 以 “.pdf” 結(jié)尾的路徑,使得別的程序想要打開網(wǎng)絡(luò) pdf 時(shí),用戶能夠可以選擇我們的程序進(jìn)行下載查看。 我們可以將 scheme 設(shè)置為 “http”,pathPattern 設(shè)置為 “.*//.pdf”,整個(gè) intent-filter 設(shè)置為:
<intent-filter> <action android:name="android.intent.action.VIEW"></action> <category android:name="android.intent.category.DEFAULT"></category> <data android:scheme="http" android:pathPattern=".*//.pdf"></data> </intent-filter>
如果你只想處理某個(gè)站點(diǎn)的 pdf,那么在 data 標(biāo)簽里增加 android:host=”yoursite.com” 則只會(huì)匹配http://yoursite.com/xxx/xxx.pdf,但這不會(huì)匹配 www.yoursite.com,如果你也想匹配這個(gè)站點(diǎn)的話,你就需要再添加一個(gè) data 標(biāo)簽,除了 android:host 改為 “www.yoursite.com” 其他都一樣。
(2)如果我們做的是一個(gè)IM應(yīng)用,或是其他類似于微博之類的應(yīng)用,如何讓別人通過 Intent 進(jìn)行調(diào)用出現(xiàn)在選擇框里呢?我們只用注冊(cè) android.intent.action.SEND 與 mimeType 為 “text/plain” 或 “/” 就可以了,整個(gè) intent-filter 設(shè)置為:
<intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data mimeType="*/*" /> </intent-filter>
這里設(shè)置 category 的原因是,創(chuàng)建的 Intent 的實(shí)例默認(rèn) category 就包含了 Intent.CATEGORY_DEFAULT ,google 這樣做的原因是為了讓這個(gè) Intent 始終有一個(gè) category。
(3)如果我們做的是一個(gè)音樂播放軟件,當(dāng)文件瀏覽器打開某音樂文件的時(shí)候,使我們的應(yīng)用能夠出現(xiàn)在選擇框里?這類似于文件關(guān)聯(lián)了,其實(shí)做起來跟上面一樣,也很簡(jiǎn)單,我們只用注冊(cè) android.intent.action.VIEW 與 mimeType 為 “audio/*” 就可以了,整個(gè) intent-filter 設(shè)置為:
<intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="audio/*" /> </intent-filter>
(3)category的匹配規(guī)則
category也是一個(gè)字符串,但是它與action的過濾規(guī)則不同,它要求Intent中個(gè)如果含有category,那么所有的category都必須和過濾規(guī)則中的其中一個(gè)category相同。也就是說,Intent中如果出現(xiàn)了category,不管有幾個(gè)category,對(duì)于每個(gè)category來說,它必須是過濾規(guī)則中的定義了的category。當(dāng)然,Intent中也可以沒有category(若Intent中未指定category,系統(tǒng)會(huì)自動(dòng)為它帶上“android.intent.category.DEFAULT”),如果沒有,仍然可以匹配成功。category和action的區(qū)別在于,action要求Intent中必須有一個(gè)action且必須和過濾規(guī)則中的某幾個(gè)action相同,而category要求Intent可以沒有category,但是一旦發(fā)現(xiàn)存在category,不論你有多少,每個(gè)都要能夠和過濾規(guī)則中的任何一個(gè)category相同。我們可以通過addCategory方法為Intent添加category。
特別說明:
<intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /></intent-filter>
這二者共同出現(xiàn),標(biāo)明該Activity是一個(gè)入口Activity,并且會(huì)出現(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 才能夠進(jìn)行匹配; pathPrefix 用來匹配路徑的開頭部分,拿上來的 Uri 來說,這里將 pathPrefix 設(shè)置為 /blog 就能進(jìn)行匹配了; pathPattern 用表達(dá)式來匹配整個(gè)路徑,這里需要說下匹配符號(hào)與轉(zhuǎn)義。 匹配符號(hào): “” 用來匹配0次或更多,如:“a” 可以匹配“a”、“aa”、“aaa”… “.” 用來匹配任意字符,如:“.” 可以匹配“a”、“b”,“c”… 因此 “.” 就是用來匹配任意字符0次或更多,如:“.html” 可以匹配 “abchtml”、“chtml”,“html”,“sdf.html”… 轉(zhuǎn)義:因?yàn)楫?dāng)讀取 Xml 的時(shí)候,“/” 是被當(dāng)作轉(zhuǎn)義字符的(當(dāng)它被用作 pathPattern 轉(zhuǎn)義之前),因此這里需要兩次轉(zhuǎn)義,讀取 Xml 是一次,在 pathPattern 中使用又是一次。如:“” 這個(gè)字符就應(yīng)該寫成 “//”,“/” 這個(gè)字符就應(yīng)該寫成 “////”。
(2)查詢是否有Activity可以匹配我們指定Intent的組件
采用PackageManager的resolveActivity或者Intent的resolveActivity方法會(huì)獲得最適合Intent的一個(gè)Activity 調(diào)用PackageManager的queryIntentActivities會(huì)返回所有成功匹配Intent的Activity
(3)android.intent.action.MAIN 與android.intent.category.LAUNCHER的區(qū)別
區(qū)別一: android.intent.action.MAIN決定一個(gè)應(yīng)用程序最先啟動(dòng)那個(gè)組件 android.intent.category.LAUNCHER決定應(yīng)用程序是否顯示在程序列表里(說白了就是是否在桌面上顯示一個(gè)圖標(biāo))這兩個(gè)屬性組合情況: 第一種情況:有MAIN,無LAUNCHER,程序列表中無圖標(biāo) 原因:android.intent.category.LAUNCHER決定應(yīng)用程序是否顯示在程序列表里 第二種情況:無MAIN,有LAUNCHER,程序列表中無圖標(biāo) 原因:android.intent.action.MAIN決定應(yīng)用程序最先啟動(dòng)的Activity,如果沒有Main,則不知啟動(dòng)哪個(gè)Activity,故也不會(huì)有圖標(biāo)出現(xiàn) 所以這兩個(gè)屬性一般成對(duì)出現(xiàn)。 如果一個(gè)應(yīng)用中有兩個(gè)組件intent-filter都添加了android.intent.action.MAIN和 android.intent.category.LAUNCHER這兩個(gè)屬性, 則這個(gè)應(yīng)用將會(huì)顯示兩個(gè)圖標(biāo), 寫在前面的組件先運(yùn)行。 區(qū)別二:
android.intent.category.LAUNCHER:android.intent.category.LAUNCHER決定應(yīng)用程序是否顯示在程序列表里,就是android開機(jī)后的主程序列表。 android.intent.category.HOME:按住“HOME”鍵,該程序顯示在HOME列表里。
(4)關(guān)于隱式intent
每一個(gè)通過 startActivity() 方法發(fā)出的隱式 Intent 都至少有一個(gè) category,就是 “android.intent.category.DEFAULT”,所以只要是想接收一個(gè)隱式 Intent 的 Activity 都應(yīng)該包括 “android.intent.category.DEFAULT” category,不然將導(dǎo)致 Intent 匹配失敗. 比如說一個(gè)activity組件要想被其他組件通過隱式intent調(diào)用, 則其在manifest.xml中的聲明如下:
<activity android:name="com.wooyun.org.MainActivity"> <intent-filter> <action android:name="com.google.test" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
(5)于intent-filter匹配優(yōu)先級(jí)
首先查看Intent的過濾器(intent-filter),按照以下優(yōu)先關(guān)系查找:action->data->category