顯式和隱式Intent介紹及intent-filter的action,category,data匹配規則
Intent型別
Intent 分為兩種型別:
顯式 Intent:按名稱(完全限定類名)指定要啟動的元件。 通常,您會在自己的應用中使用顯式 Intent 來啟動元件,這是因為您知道要啟動的 Activity 或服務的類名。例如,啟動新 Activity 以響應使用者操作,或者啟動服務以在後臺下載檔案。
建立顯式 Intent 啟動 Activity 或服務時,系統將立即啟動 Intent 物件中指定的應用元件。
隱式 Intent :不會指定特定的元件,而是宣告要執行的常規操作,從而允許其他應用中的元件來處理它。 例如,如需在地圖上向用戶顯示位置,則可以使用隱式 Intent,請求另一具有此功能的應用在地圖上顯示指定的位置。
建立隱式 Intent 時,Android 系統通過將 Intent 的內容與在裝置上其他應用的清單檔案中宣告的 Intent 過濾器進行比較,從而找到要啟動的相應元件。 如果 Intent 與 Intent 過濾器匹配,則系統將啟動該元件,並向其傳遞 Intent 物件。 如果多個 Intent 過濾器相容,則系統會顯示一個對話方塊,支援使用者選取要使用的應用。
構建 Intent
Intent 中包含的主要資訊如下:
1. 元件名稱
要啟動的元件名稱。這是可選項,但也是構建顯式 Intent 的一項重要資訊,這意味著 Intent 應當僅傳遞給由元件名稱定義的應用元件。 如果沒有元件名稱,則 Intent 是隱式的。
Intent 的這一欄位是一個 ComponentName 物件,您可以使用目標元件的完全限定類名指定此物件,其中包括應用的軟體包名稱。 例如, com.example.ExampleActivity。您可以使用 setComponent()、setClass()、setClassName() 或 Intent 建構函式設定元件名稱。
2. action
指定要執行的通用操作(例如,“檢視”或“選取”)的字串。您可以指定自己的操作,供 Intent 在您的應用內使用(或者供其他應用在您的應用中呼叫元件)。但是,您通常應該使用由 Intent 類或其他框架類定義的操作常量。如一些常用action:
ACTION_VIEW
ACTION_SEND
3. data
引用待操作資料和/或該資料 MIME 型別的 URI(Uri 物件)。提供的資料型別通常由 Intent 的操作決定。例如,如果操作是 ACTION_EDIT,則資料應包含待編輯文件的 URI。
4. Category
一個包含應處理 Intent 元件型別的附加資訊的字串。以下是一些常見類別:
CATEGORY_BROWSABLE
,目標 Activity 允許本身通過網路瀏覽器啟動,以顯示連結引用的資料,如影象或電子郵件。
CATEGORY_LAUNCHER
,該 Activity 是任務的初始 Activity,在系統的應用啟動器中列出。
5. Extra
攜帶完成請求操作所需的附加資訊的鍵值對。您可以使用各種 putExtra() 方法新增 extra 資料,每種方法均接受兩個引數:鍵名和值。您還可以建立一個包含所有 extra 資料的 Bundle 物件,然後使用 putExtras() 將Bundle 插入 Intent 中。
6. IntentFlag
在 Intent 類中定義的、充當 Intent 元資料的標誌。標誌可以指示 Android 系統如何啟動 Activity(例如,Activity 應屬於哪個任務),以及啟動之後如何處理(例如,它是否屬於最近的 Activity 列表)。具體可以檢視我之前寫的一篇文章。IntentFlag大全及使用總結
顯式 Intent 示例:
// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);
隱式 Intent 示例:
// Create the text message with a string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");
// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
Intent Filter匹配規則
Intent解析機制主要是通過查詢已註冊在AndroidManifest.xml中的所有IntentFilter及其中定義的Intent,最終找到匹配的Intent。一個元件可以宣告多個Intent Filter,只需要匹配任意一個即可啟動該元件。 一個Intent Filter中的action、type、category可以有多個,所有的action、type、category分別構成不同類別,同一類別資訊共同約束當前類別的匹配過程。只有一個Intent同時匹配一個Intent Filter的action、type、category這三個類別才算完全匹配,只有完全匹配才能啟動Activity。
比如下面定義了兩個intent-filter,只要有一個Intent Filter的action、type、category完全匹配即可:
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="http" android:mimeType="video/*" />
</intent-filter>
<intent-filter>
<action android:name="com.study.jankin.test"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="demoapp" />
</intent-filter>
1. action的匹配規則
一個Intent Filter中可宣告多個action,Intent中的action與其中的任一個action在字串形式上完全相同(注意,區分大小寫,大小寫不同但字串內容相同也會造成匹配失敗),action方面就匹配成功。
注意,隱式Intent必須指定action。
比如我們在Manifest檔案中為Activity定義瞭如下Intent Filter:
<intent-filter>
<action android:name="com.study.jankin.test"/>
</intent-filter>
在程式中我們就可以通過下面的程式碼啟動該Activity
Intent intent = new Intent("com.study.jankin.test");
startActivity(intent);
2. category的匹配規則
category也是一個字串,但是它與action的過濾規則不同,它要求Intent中個如果含有category,那麼所有的category都必須和過濾規則中的其中一個category相同。也就是說,Intent中如果出現了category,不管有幾個category,對於每個category來說,它必須是過濾規則中的定義了的category。當然,Intent中也可以沒有category(若Intent中未指定category,系統會自動為它帶上“android.intent.category.DEFAULT”),如果沒有,仍然可以匹配成功。我們可以通過addCategory方法為Intent新增category。
如常用的 <category android:name="android.intent.category.BROWSABLE"/>
可以讓元件通過瀏覽器啟動。
3. data的匹配規則
同action類似,如果過濾規則中定義了data, 那麼Intent中必須也要定義可匹配的data,只要Intent的data與Intent Filter中的任一個data宣告完全相同,data方面就完全匹配成功。
data由兩部分組成:mimeType和Uri。
MineType指的是媒體型別:例如imgage/jpeg,auto/mpeg4和viedo/*等,可以表示圖片、文字、視訊等不同的媒體格式
Uri 可配置更多資訊,類似於url。Uri的預設scheme是content或file。
URI的結構:
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
content://com.axe.mg:100/fold/subfolder/etc
http://www.axe.com:500/profile/info
Uri的屬性說明:
Scheme:URI的模式,比如http、file、content。如果URI中沒有指定Scheme,那麼整個URI無效。預設值為content 和 file。
Host:URI的主機名。比如www.baidu.com,如果host未指定,那麼整個Uri中的其他引數無效,這也意味著Uri是無效的。
Port:URI埠,當URI指定了scheme 和 host 引數時port引數才有意義。
path:用來匹配完整的路徑,如:http://example.com/blog/abc.html,這裡將 path 設定為 /blog/abc.html 才能夠進行匹配;
pathPrefix: 用來匹配路徑的開頭部分,拿上面的 Uri 來說,這裡將 pathPrefix 設定為 /blog 就能進行匹配了;
pathPattern: 用表示式來匹配整個路徑。這裡需要說下匹配符號與轉義。
匹配符號:
1、“” 用來匹配0次或更多,如:“a” 可以匹配“a”、“aa”、“aaa”…
2、“.” 用來匹配任意字元,如:“.” 可以匹配“a”、“b”,“c”…
3、因此 “.*” 就是用來匹配任意字元0次或更多,如:“.*html” 可以匹配 “abchtml”、“chtml”,“html”,“sdf.html”…
轉義:
因為當讀取 Xml 的時候,“\” 是被當作轉義字元的(當它被用作 pathPattern 轉義之前),因此這裡需要兩次轉義,讀取 Xml 是一次,在 pathPattern 中使用又是一次。如:“*”
這個字元就應該寫成 “\\*”
,“\”
這個字元就應該寫成 “\\\\”
。
Intent的uri可通過setData()方法設定,mimetype可通過setType()方法設定。
注意:這兩個方法會互相清除對方設定的值,如果需要同時設定uri和mimetype可以呼叫setDataAndType()方法
這三個方法的原始碼如下:
public @NonNull Intent setData(@Nullable Uri data) {
mData = data;
mType = null;
return this;
}
public @NonNull Intent setType(@Nullable String type) {
mData = null;
mType = type;
return this;
}
public @NonNull Intent setDataAndType(@Nullable Uri data, @Nullable String type) {
mData = data;
mType = type;
return this;
}
下面看一個例子,清單檔案中該Activity的intent-filter配置如下:
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.study.jankin.test"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<data
android:host="www.jankin.com"
android:pathPattern=".*\\.pdf"
android:scheme="http"/>
</intent-filter>
</activity>
我們可以通過如下程式碼啟動該Activity:
Intent intent = new Intent("com.study.jankin.test");
intent.setData(Uri.parse("http://www.jankin.com/a/b/a.pdf"));
if(intent.resolveActivity(getPackageManager()) != null){
startActivity(intent);
}
Intent Filter常見問題
1. 查詢是否有Activity可以匹配我們指定Intent的元件
- 採用PackageManager的resolveActivity或者Intent的resolveActivity方法會獲得最適合Intent的一個Activity。
- 呼叫PackageManager的queryIntentActivities會返回所有成功匹配Intent的Activity。
其中PackageManager中queryIntentActivities和resolveActivity的方法原型:
public abstract List<ResolveInfo> queryIntentActivities(Intent intent, @ResolveInfoFlags int flags);
public abstract ResolveInfo resolveActivity(Intent intent, @ResolveInfoFlags int flags);
上述兩個方法的第一個引數比較好理解,第二個引數要注意,要使用PackageManager.MATCH_DEFAULT_ONLY
這個標記位,這個標記位的含義是僅僅匹配那些在intent-filter中宣告<category android:name="android.intent.category.DEFAULT"/>
這個category的Activity。使用這個標記位可以保證我們只要上述兩個方法不返回null,那麼startActivity一定可以成功。如果不用這個標記位,就可以把intent-filter中category不含default的那些Activity匹配出來,因為不含有default這個category的Activity無法接收隱式Intent,從而可能導致startActivity失敗。
2. 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,故也不會有圖標出現
所以這兩個屬性一般成對出現。
3. 關於隱式intent
每一個通過 startActivity() 方法發出的隱式 Intent 都至少有一個 category,就是 “android.intent.category.DEFAULT”,所以只要是想接收一個隱式 Intent 的 Activity 都應該包括 “android.intent.category.DEFAULT” category,不然將導致 Intent 匹配失敗.
參考
https://developer.android.com/guide/components/intents-filters?hl=zh-cn
https://blog.csdn.net/mynameishuangshuai/article/details/51673273