1. 程式人生 > >顯式和隱式Intent介紹及intent-filter的action,category,data匹配規則

顯式和隱式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

https://www.jianshu.com/p/5f644e0fdba9