你必須弄懂的Intent Filter匹配規則

abcjob發表於2021-09-09
Intent簡介

Android中提供了Intent機制來協助應用間的互動與通訊,Intent負責對應用中一次操作的動作、動作涉及資料、附加資料進行描述,Android則根據此Intent的描述,負責找到對應的元件,將 Intent傳遞給呼叫的元件,並完成元件的呼叫。Intent不僅可用於應用程式之間,也可用於應用程式內部的Activity/Service之間的互動。因此,Intent在這裡起著一個媒體中介的作用,專門提供元件互相呼叫的相關資訊,實現呼叫者與被呼叫者之間的解耦。在SDK中給出了Intent作用的表現形式為:

  • 透過Context.startActivity() orActivity.startActivityForResult() 
    啟動一個Activity;
  • 透過 Context.startService() 啟動一個服務,或者透過Context.bindService() 和後臺服務互動;
  • 透過廣播方法(比如 Context.sendBroadcast(),Context.sendOrderedBroadcast(), 
    Context.sendStickyBroadcast()) 發給broadcast receivers。

    Intent可分為隱式(implicitly)和顯式(explicitly)兩種:

(1)顯式 Intent

即在構造Intent物件時就指定接收者,它一般用在知道目標元件名稱的前提下,一般是在相同的應用程式內部實現的,如下:

Intent intent = new Intent(MainActivit.this, NewActivity.class);
startActivity(intent );  

上面那個intent中,直接指明瞭接收者:NewActivity

(2)隱式 Intent

即Intent的傳送者在構造Intent物件時,並不知道也不關心接收者是誰,有利於降低傳送者和接收者之間的耦合,它一般用在沒有明確指出目標元件名稱的前提下,一般是用於在不同應用程式之間,如下:

Intent intent = new Intent();
intent.setAction("com.wooyun.test");
startActivity(intent);

上面那個intent,沒有指明接收者,只是給了一個action作為接收者的過濾條件。 

對於顯式Intent,Android不需要去做解析,因為目標元件已經很明確,Android需要解析的是那些隱式Intent,透過解析,將Intent對映給可以處理此Intent的Activity、IntentReceiver或Service。

Intent Filter匹配規則

Intent解析機制主要是透過查詢已註冊在AndroidManifest.xml中的所有IntentFilter及其中定義的Intent,最終找到匹配的Intent。在這個解析過程中,Android是透過Intent的action、type、category這三個屬性來進行匹配判斷的。一個過濾列表中的action、type、category可以有多個,所有的action、type、category分別構成不同類別,同一類別資訊共同約束當前類別的匹配過程。只有一個Intent同時匹配action、type、category這三個類別才算完全匹配,只有完全匹配才能啟動Activity。另外一個元件若宣告瞭多個Intent Filter,只需要匹配任意一個即可啟動該元件。 
例如:

(1)action的匹配規則

action是一個字串,如果Intent指明定了action,則目標元件的IntentFilter的action列表中就必須包含有這個action,否則不能匹配。一個Intent Filter中可宣告多個action,Intent中的action與其中的任一個action在字串形式上完全相同(注意,區分大小寫,大小寫不同但字串內容相同也會造成匹配失敗),action方面就匹配成功。可透過setAction方法為Intent設定action,也可在構造Intent時傳入action。需要注意的是,隱式Intent必須指定action。比如我們在Manifest檔案中為MyActivity定義瞭如下Intent Filter:

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

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

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

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

Intent.ACTION_VIEW
Intent.ACTION_DIAL
Intent.ACTION_SENDTO
Intent.ACTION_SEND
Intent.ACTION_WEB_SEARCH

(2)data的匹配規則

如果Intent沒有提供type,系統將從data中得到資料型別。和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部分組成

://:/[||]

例如: 
       content://com.wooyun.org:200/folder/etc 
       

Intent的uri可透過setData方法設定,mimetype可透過setType方法設定。 

需要注意的是:若Intent Filter的data宣告部分未指定uri,則預設uri為content或file,Intent中的uri的scheme部分需為content或file才能匹配;若要為Intent指定完整的data,必須用setDataAndType方法,究其原因在,setData和setType方法的原始碼中我們發現:

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

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

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


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

使用案例: 
(1)如果我們想要匹配 http 以 “.pdf” 結尾的路徑,使得別的程式想要開啟網路 pdf 時,使用者能夠可以選擇我們的程式進行下載檢視。 
我們可以將 scheme 設定為 “http”,pathPattern 設定為 “.*//.pdf”,整個 intent-filter 設定為:

,但這不會匹配 ,如果你也想匹配這個站點的話,你就需要再新增一個 data 標籤,除了 android:host 改為 “” 其他都一樣。 

(2)如果我們做的是一個IM應用,或是其他類似於微博之類的應用,如何讓別人透過 Intent 進行呼叫出現在選擇框裡呢?我們只用註冊 android.intent.action.SEND 與 mimeType 為 “text/plain” 或 “/” 就可以了,整個 intent-filter 設定為:

這裡設定 category 的原因是,建立的 Intent 的例項預設 category 就包含了 Intent.CATEGORY_DEFAULT ,google 這樣做的原因是為了讓這個 Intent 始終有一個 category。

圖片描述

(3)如果我們做的是一個音樂播放軟體,當檔案瀏覽器開啟某音樂檔案的時候,使我們的應用能夠出現在選擇框裡?這類似於檔案關聯了,其實做起來跟上面一樣,也很簡單,我們只用註冊 android.intent.action.VIEW 與 mimeType 為 “audio/*” 就可以了,整個 intent-filter 設定為:

圖片描述

(3)category的匹配規則

category也是一個字串,但是它與action的過濾規則不同,它要求Intent中個如果含有category,那麼所有的category都必須和過濾規則中的其中一個category相同。也就是說,Intent中如果出現了category,不管有幾個category,對於每個category來說,它必須是過濾規則中的定義了的category。當然,Intent中也可以沒有category(若Intent中未指定category,系統會自動為它帶上“android.intent.category.DEFAULT”),如果沒有,仍然可以匹配成功。category和action的區別在於,action要求Intent中必須有一個action且必須和過濾規則中的某幾個action相同,而category要求Intent可以沒有category,但是一旦發現存在category,不論你有多少,每個都要能夠和過濾規則中的任何一個category相同。我們可以透過addCategory方法為Intent新增category。

特別說明:

這二者共同出現,標明該Activity是一個入口Activity,並且會出現在系統應用列表中,二者缺一不可。

Intent Filter常見問題彙總

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

path 用來匹配完整的路徑,如:http://example.com/blog/abc.html,這裡將 path 設定為 /blog/abc.html 才能夠進行匹配; 
pathPrefix 用來匹配路徑的開頭部分,拿上來的 Uri 來說,這裡將 pathPrefix 設定為 /blog 就能進行匹配了; 
pathPattern 用表示式來匹配整個路徑,這裡需要說下匹配符號與轉義。 
匹配符號: 
” 用來匹配0次或更多,如:“a” 可以匹配“a”、“aa”、“aaa”… 
“.” 用來匹配任意字元,如:“.” 可以匹配“a”、“b”,“c”… 
因此 “.” 就是用來匹配任意字元0次或更多,如:“.html” 可以匹配 “abchtml”、“chtml”,“html”,“sdf.html”… 
轉義:因為當讀取 Xml 的時候,“/” 是被當作跳脫字元的(當它被用作 pathPattern 轉義之前),因此這裡需要兩次轉義,讀取 Xml 是一次,在 pathPattern 中使用又是一次。如:“” 這個字元就應該寫成 “//”,“/” 這個字元就應該寫成 “////”。

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

採用PackageManager的resolveActivity或者Intent的resolveActivity方法會獲得最適合Intent的一個Activity 
呼叫PackageManager的queryIntentActivities會返回所有成功匹配Intent的Activity

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

區別一: 

android.intent.action.MAIN決定一個應用程式最先啟動那個元件 
android.intent.category.LAUNCHER決定應用程式是否顯示在程式列表裡(說白了就是是否在桌面上顯示一個圖示) 
這兩個屬性組合情況:
 
  第一種情況:有MAIN,無LAUNCHER,程式列表中無圖示 
原因:android.intent.category.LAUNCHER決定應用程式是否顯示在程式列表裡 

第二種情況:無MAIN,有LAUNCHER,程式列表中無圖示 
原因:android.intent.action.MAIN決定應用程式最先啟動的Activity,如果沒有Main,則不知啟動哪個Activity,故也不會有圖示出現 ,所以這兩個屬性一般成對出現。

如果一個應用中有兩個元件intent-filter都新增了android.intent.action.MAIN和 
android.intent.category.LAUNCHER這兩個屬性, 則這個應用將會顯示兩個圖示, 寫在前面的元件先執行。 

區別二:

android.intent.category.LAUNCHER:android.intent.category.LAUNCHER決定應用程式是否顯示在程式列表裡,就是android開機後的主程式列表。 
android.intent.category.HOME:按住“HOME”鍵,該程式顯示在HOME列表裡。

(4)關於隱式intent

每一個透過 startActivity() 方法發出的隱式 Intent 都至少有一個 category,就是 “android.intent.category.DEFAULT”,所以只要是想接收一個隱式 Intent 的 Activity 都應該包括 “android.intent.category.DEFAULT” category,不然將導致 Intent 匹配失敗. 
比如說一個activity元件要想被其他元件透過隱式intent呼叫, 則其在manifest.xml中的宣告如下:

http://blog.csdn.net/cnnumen/article/details/8464786 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4798/viewspace-2799789/,如需轉載,請註明出處,否則將追究法律責任。

相關文章