1. 程式人生 > >Android 開發中 Intent 和 Intent 過濾器學習筆記

Android 開發中 Intent 和 Intent 過濾器學習筆記

Android 中的 Intent 是一個訊息傳遞物件,我們可以使用它來從其他應用,元件等發起和傳遞操作請求。其主要使用場景包含如下三種:

  • 啟動 Activity:
    通過將 Intent 傳遞給 startActivity(),我們可以啟動新的 Activity 例項。Intent 描述了要啟動的 Activity,並攜帶了啟動Activity必要的資料,如:初始狀態等等。如果我們想要在 Activity 完成後收到結果,需要呼叫 startActivityForResult() 來啟動Activity,並在 Activity 的 onActivityResult() 回撥中處理相關結果。

  • 啟動服務:
    通過將 Intent 傳遞給 startService(),我們可以啟動服務執行一次性操作(例如,下載檔案)。Intent 描述了要啟動的服務,並攜帶了必要的資料。

  • 傳遞廣播:
    通過將 Intent 傳遞給 sendBroadcast()、sendOrderedBroadcast() 或 sendStickyBroadcast(),您可以將廣播傳遞給其他應用。(廣播是任何應用均可接收的訊息。例如:系統針對系統事件(如:系統啟動,裝置開始充電等狀態事件)進行廣播。)

Intent 型別分類

Intent 分為兩種型別:

  • 顯式 Intent:
    按名稱(完全限定類名)指定要啟動的元件(Activity,Service)。 當我們知道要啟動的 Activity 或 Service 的類名時,我們通常使用顯式 Intent 來啟動元件。建立顯示的Intent示例:
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);
  • 隱式 Intent :
    不會指定特定的元件,而是宣告要執行的常規操作,從而允許其他應用中的元件來處理它。 例如,如需在地圖上顯示使用者位置,則可以使用隱式 Intent,請求具有此功能的應用來完成這項任務。建立隱式的Intent示例:
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 啟動 Activity 或服務時,系統將立即啟動 Intent 物件中指定的應用元件。

建立隱式 Intent 時,Android 系統通過將 Intent 的內容與在裝置上其他應用的清單檔案(Application/AndroidManifest.xml)中宣告的 Intent 過濾器進行比較,從而找到要啟動的相應元件。 如果 Intent 與 Intent 過濾器匹配,則系統將啟動該元件,並向其傳遞 Intent 物件。 如果存在多個 Intent 過濾器相容,則系統會顯示一個對話方塊,讓使用者決定選取要使用的應用,如:我們安裝了多個瀏覽器,當發起一個網頁顯示的 Intent 請求時,會讓使用者決定使用什麼瀏覽器開啟網頁。

Intent 過濾器是應用清單檔案中的一個表示式,它指定該元件要接收的 Intent 型別。通過為 Activity 宣告 Intent 過濾器,我們可以使得其他應用能夠直接使用某一特定型別的 Intent 來啟動我們配置好的 Activity。如果我們沒有為 Activity 宣告任何 Intent 過濾器,則該 Activity 只能通過顯式 Intent 啟動。例如下面的程式碼片段中,我們配置了包含 intent-filter 的Activity,這樣其他應用就可以通過隱式 Intent 來啟動該Activity。

<activity
    android:name=".wxapi.WXEntryActivity"
    android:label="@string/launcher_name"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="WECHATAPPID"/>
    </intent-filter>
</activity>

PS: 為了確保應用的安全性,啟動 Service 時,請始終使用顯式 Intent,且不要為服務宣告 Intent 過濾器。使用隱式 Intent 啟動服務存在安全隱患,因為我們無法確定哪些服務將響應 Intent,且使用者無法看到哪些服務已啟動,這樣的機制很可能被惡意軟體利用。從 Android 5.0(API 級別 21)開始,如果使用隱式 Intent 呼叫 bindService(),系統會引發異常。

Intent 啟動Activity的過程示例

先來看看一張官方的示列圖(隱式 Intent 如何通過系統傳遞以啟動其他 Activity 的圖解):
隱式 Intent 如何通過系統傳遞以啟動其他 Activity 的圖解

  1. Activity A 建立包含操作描述的 Intent,並將其傳遞給 startActivity()。
  2. Android 系統搜尋所有應用中與 Intent 匹配的 Intent 過濾器。
  3. 找到匹配項之後,該系統通過呼叫匹配 Activity(Activity B)的 onCreate() 方法並將其傳遞給 Intent,以此啟動匹配到的 Activity。

構建可用的 Intent

Intent 物件攜帶了幫助系統底層用來確定要啟動的元件的描述資訊(如:準確的元件名稱或應當接收該 Intent 的元件類別),以及響應元件在接收到 Intent 之後要採取的操作以及要處理的資料等資訊。

Intent 中包含的主要資訊如下:

  • 元件名稱(目標元件的完全限定類名,可選,型別是:android.content.ComponentName)
    要啟動的元件名稱。如果需要構建顯式 Intent 這是必選項,這意味著 Intent 只能傳遞給由元件名稱定義的應用元件。 如果沒有指定元件名稱,則構建出來的 Intent 是隱式的,且系統將根據其他 Intent 資訊,如:Intent 的操作、資料和類別,來決定哪個元件應當接收 Intent。例如我們想要啟動 com.example.ExampleActivity 這個元件。我們可以使用 Intent 例項的 setComponent()、setClass()、setClassName() 方法來填充元件名稱,或在構建 Intent 的時候顯示的向建構函式傳遞元件名稱。PS:當需要使用 Intent 來啟動 Service 時,應該始終指定元件名稱,使得 Intent 始終是顯示的。

  • 操作
    指定要執行的通用操作(例如,“檢視”或“選取”)的字串。使用 setAction() 或 Intent 建構函式為 Intent 指定操作。對於用於廣播的 Intent,操作是指已發生且正在報告的事件。操作在很大程度上決定了其 Intent 的構成,特別是資料和 extra 中包含的內容。Intent 類內建了幾類常見的操作常量, 如:ACTION_VIEWACTION_SEND。 當然我們也可以指定自己的操作,供 Intent 在應用內或者供其他應用在我們自己的應用中呼叫相關元件來響應操作。如果定義自己的操作型別,最佳實踐是確保將應用的軟體包名稱作為字首。

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
  • 資料
    引用待操作資料和/或該資料 MIME 型別的 URI(Uri 物件)。具體的資料型別通常由 Intent 的操作決定。例如,如果操作是 ACTION_EDIT,則資料應包含待編輯文件的 URI。建立 Intent 時,除了指定 URI 以外,指定資料型別(其 MIME 型別)往往也很重要。例如,能夠顯示影象的 Activity 可能無法播放音訊檔案,即便 URI 格式十分類似時也是如此。因此,指定資料的 MIME 型別有助於系統找到接收 Intent 的最佳元件。當 MIME 型別可以從 URI 中推斷得出時,系統底層會幫助我們進行判斷,可不制定資料型別。呼叫 setData() 設定資料 URI。呼叫 setType() 設定 MIME 型別。使用 setDataAndType() 同時顯式設定二者。注意:若要同時設定 URI 和 MIME 型別,請始終使用 setDataAndType()。

  • 類別
    使用 addCategory() 指定類別, 類別是一個包含應處理 Intent 元件型別的附加資訊的字串。 可以將任意數量的類別描述放入一個 Intent 中,但大多數 Intent 均不需要類別。一些常見類別 CATEGORY_BROWSABLE – 目標 Activity 允許本身通過網路瀏覽器啟動,以顯示連結引用的資料,如影象或電子郵件。CATEGORY_LAUNCHER – 目標 Activity 是任務的初始 Activity,在系統的應用啟動器中列出。

通過上面我們羅列出的表示 Intent 的既定特徵的資訊:元件名稱、操作、資料和類別等資訊。Android 系統能夠通過讀取這些屬性並決定應當啟動哪個應用元件來響應 Intent 訊息。當然,Intent 也可以攜帶其他不影響系統解析的額外資訊。如:

  • Extra
    攜帶完成請求操作所需的附加資訊的鍵值對。正如某些操作使用特定型別的資料 URI 一樣,有些操作也使用特定的 extra。可以使用各種 putExtra() 方法新增 extra 資料,每種方法均接受兩個引數:鍵名和值。您還可以建立一個包含所有 extra 資料的 Bundle 物件,然後使用 putExtras() 將Bundle 插入 Intent 中。例如,使用 ACTION_SEND 建立用於傳送電子郵件的 Intent 時,可以使用 EXTRA_EMAIL 鍵指定“目標”收件人,並使用 EXTRA_SUBJECT 鍵指定“主題”。
    Intent 類將為標準化的資料型別指定多個 EXTRA_* 常量。如需宣告自己的 extra 鍵(對於應用接收的 Intent),請確保將應用的軟體包名稱作為字首。 例如:
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
  • 標誌
    呼叫setFlags() 方法設定,充當 Intent 元資料的標誌。 標誌可以指示 Android 系統如何啟動 Activity(例如,Activity 應屬於哪個任務),以及啟動之後如何處理後續操作(例如,它是否屬於最近的 Activity 列表)。