意圖和意圖篩選器

Intent 是訊息物件,可用來要求其他應用程式元件執行動作。雖然意圖可以透過多種方式協助元件之間進行通訊,但您仍可採行以下三種基本使用情境:

  • 啟動活動

    Activity 代表應用程式中的單一畫面。您可以將 Intent 傳遞至 startActivity(),以啟動 Activity 的新執行個體。Intent 會描述要啟動並傳輸任何必要資料的活動。

    如要在活動完成後收到活動的結果,請呼叫 startActivityForResult()。活動會在活動的 onActivityResult() 回呼中,以個別 Intent 物件的形式接收結果。詳情請參閱活動指南。

  • 啟動服務

    Service 是一種元件,不需要使用者介面即可在背景執行作業。在 Android 5.0 (API 級別 21) 以上版本中,您可以使用 JobScheduler 啟動服務。如要進一步瞭解 JobScheduler,請參閱其 API-reference documentation

    如果是 Android 5.0 (API 級別 21) 以下版本,您可以使用 Service 類別的方法啟動服務。您可以將 Intent 傳遞至 startService(),來啟動服務來執行一次性作業 (例如下載檔案)。Intent 會說明啟動並承載任何必要資料的服務。

    如果服務是以用戶端伺服器介面設計,您可以將 Intent 傳遞至 bindService(),從其他元件繫結至服務。詳情請參閱服務指南。

  • 提交廣播

    廣播訊息是所有應用程式可以接收的訊息。系統會為系統事件提供各種廣播訊息,例如系統啟動或裝置開始充電時。您可以將 Intent 傳遞至 sendBroadcast()sendOrderedBroadcast(),向其他應用程式傳遞廣播。

本頁面其餘部分說明瞭意圖的運作方式及使用方法。如需相關資訊,請參閱「與其他應用程式互動」和「分享內容」。

意圖類型

意圖分為兩種類型:

  • 「明確意圖」可提供目標應用程式的套件名稱或完整的元件類別名稱,指定哪些應用程式能滿足意圖。一般來說,您會使用明確意圖,在自家應用程式中啟動元件,因為您知道要啟動的活動或服務的類別名稱。舉例來說,您可以在應用程式中啟動新活動以回應使用者動作,或啟動服務在背景下載檔案。
  • 「隱含意圖」不會命名特定元件,而是宣告要執行的一般動作,讓其他應用程式的元件能夠處理。舉例來說,如果您想在地圖上向使用者顯示位置資訊,您可以使用隱含意圖要求其他可操作的應用程式顯示地圖上的指定位置。

圖 1 顯示啟動活動時如何使用意圖。當 Intent 物件明確命名特定活動元件時,系統會立即啟動該元件。

圖 1 如何透過系統提供隱含意圖以啟動其他活動:[1] Activity A 會建立含有動作說明的 Intent,並傳遞至 startActivity()[2] Android 系統會搜尋所有應用程式,尋找符合意圖的意圖篩選器。找到相符項目時,[3] 系統會叫用其 onCreate() 方法並傳遞 Intent,藉此啟動相符的活動 (活動 B)。

當您使用隱含意圖時,Android 系統會尋找適當的元件,先從裝置上其他應用程式資訊清單檔案中宣告的意圖篩選器進行比較。如果意圖符合意圖篩選器,系統會啟動該元件並向 Intent 物件提供。如果與多個意圖篩選器相容,系統會顯示對話方塊,讓使用者挑選要使用的應用程式。

意圖篩選器是應用程式資訊清單檔案中的運算式,用於指定元件會接收的意圖類型。例如,為某項活動宣告意圖篩選器,可讓其他應用程式直接以特定類型的意圖啟動活動。同樣地,如果您「沒有」宣告活動的任何意圖篩選器,就只能以明確意圖啟動。

注意:為確保應用程式安全無虞,啟動 Service 時請一律使用明確意圖,且不宣告服務的意圖篩選器。使用隱含意圖啟動服務會造成安全性危害,因為您無法確定哪一項服務會回應意圖,且使用者也看不到啟動的服務。從 Android 5.0 (API 級別 21) 開始,如果您使用隱含意圖呼叫 bindService(),系統會擲回例外狀況。

建立意圖

Intent 物件會包含 Android 系統用來判斷要啟動元件的資訊 (例如應接收意圖的確切元件名稱或元件類別),以及接收方元件用來正確執行動作的資訊 (例如要執行的動作和要處理的資料)。

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

元件名稱
要啟動的元件名稱。

這是選用項目,但是讓意圖「明確」的關鍵資訊。也就是說,意圖只會傳送給元件名稱所定義的應用程式元件。如果沒有元件名稱,意圖就會「隱含」,且系統會根據下述的其他意圖資訊 (如下方所述動作、資料和類別) 決定元件要接收意圖的元件。如果您需要在應用程式中啟動特定元件,請指定元件名稱。

注意:啟動 Service 時,請一律指定元件名稱。否則,您無法確定哪些服務會回應意圖,也無法讓使用者查看啟動了哪些服務。

Intent 的這個欄位是 ComponentName 物件,您可以使用目標元件的完整類別名稱進行指定,包括應用程式的套件名稱,例如 com.example.ExampleActivity。您可以使用 setComponent()setClass()setClassName()Intent 建構函式設定元件名稱。

動作片
這個字串可指定要執行的一般動作 (例如「view」或「pick」)。

以廣播意圖來說,這是指已執行的動作並系統會回報。動作主要會影響其餘意圖的結構,特別是資料中和額外項目中的資訊。

您可以指定自己的動作,供應用程式內的意圖使用 (或供其他應用程式使用以叫用應用程式中的元件),但通常會指定 Intent 類別或其他架構類別定義的動作常數。以下是啟動活動的一些常見動作:

ACTION_VIEW
當您有可向使用者顯示的活動資訊 (例如可在圖片庫應用程式中查看的相片,或是可在地圖應用程式中查看的地址),請在意圖中使用 startActivity() 執行這項操作。
ACTION_SEND
也稱為「分享」意圖,當您擁有使用者可透過其他應用程式分享的資料 (例如電子郵件應用程式或社群媒體分享應用程式),應在意圖中使用 startActivity() 這個意圖。

如要進一步瞭解定義一般動作的常數,請參閱 Intent 類別參考資料。其他動作則在 Android 架構的其他位置定義,例如 Settings 中用於在系統「設定」應用程式中開啟特定畫面的動作。

您可以使用 setAction()Intent 建構函式指定意圖的動作。

如果您定義自己的動作,請務必將應用程式的套件名稱做為前置字串,如以下範例所示:

Kotlin

const val ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL"

Java

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
資料
參照資料並/或 MIME 類型的 URI (Uri 物件)。提供的資料類型通常取決於意圖的動作。舉例來說,如果動作是 ACTION_EDIT,資料應包含要編輯的文件 URI。

建立意圖時,除了 URI 以外,指定資料類型 (其 MIME 類型) 通常也很重要。舉例來說,即使 URI 格式可能類似,能夠顯示圖片的活動也可能無法播放音訊檔案。指定資料的 MIME 類型,可協助 Android 系統找到最佳元件來接收您的意圖。不過,有時可以從 URI 推斷 MIME 類型,尤其是在資料是 content: URI 時。content: URI 表示資料位於裝置上,並由 ContentProvider 控管,因此系統會向使用者顯示資料 MIME 類型。

如果只要設定資料 URI,請呼叫 setData()。如果只要設定 MIME 類型,請呼叫 setType()。如有需要,您可以使用 setDataAndType() 明確設定兩者。

注意:如要同時設定 URI 和 MIME 類型,「不要」呼叫 setData()setType(),因為這兩者都會將另一個值的值變成空值。一律使用 setDataAndType() 來設定 URI 和 MIME 類型。

類別
此字串包含應處理意圖的元件種類額外資訊。意圖中可以置入任意數量的類別說明,但多數意圖不需要類別。常見的類別如下:
CATEGORY_BROWSABLE
可讓網路瀏覽器自行啟動目標活動,顯示連結參照的資料,例如圖片或電子郵件。
CATEGORY_LAUNCHER
活動是任務的初始活動,會顯示在系統的應用程式啟動器中。

如需完整的類別清單,請參閱 Intent 類別說明。

您可以使用 addCategory() 指定類別。

上述這些屬性 (元件名稱、動作、資料和類別) 代表意圖的定義特性。讀取這些屬性後,Android 系統就能解析應啟動哪個應用程式元件。不過,意圖可包含的額外資訊,這不會影響到應用程式元件解析的方式。意圖也可以提供下列資訊:

額外內容
提供完成要求動作所需的額外資訊的鍵/值組合。就像某些動作會使用特定類型的資料 URI,有些動作也會使用特定的額外項目。

您可以透過各種 putExtra() 方法新增額外資料,每種方法都接受兩個參數:鍵名稱和值。您也可以建立包含所有額外資料的 Bundle 物件,然後使用 putExtras()Intent 中插入 Bundle

舉例來說,如要建立使用 ACTION_SEND 傳送電子郵件的意圖,您可以使用 EXTRA_EMAIL 鍵指定「收件者」收件者,並使用 EXTRA_SUBJECT 鍵指定 主旨

Intent 類別會為標準化資料類型指定多個 EXTRA_* 常數。如果您需要宣告自己的額外金鑰 (適用於應用程式收到的意圖),請務必將應用程式的套件名稱做為前置字串加入,如以下範例所示:

Kotlin

const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"

Java

static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";

注意:傳送您預期其他應用程式接收的意圖時,請勿使用 ParcelableSerializable 資料。如果應用程式嘗試存取 Bundle 物件中的資料,但無法存取已包裝或序列化的類別,系統會發出 RuntimeException

旗標
旗標是在 Intent 類別中定義,該類別會做為意圖的中繼資料。旗標可指示 Android 系統如何啟動活動 (例如活動應屬於哪個工作),以及活動啟動後的處理方式 (例如是否屬於近期活動清單)。

詳情請參閱 setFlags() 方法。

明確意圖範例

明確意圖是用來啟動特定應用程式元件,例如應用程式中的特定活動或服務。如要建立明確意圖,請定義 Intent 物件的元件名稱,所有其他意圖屬性則為選用。

舉例來說,如果您在應用程式中建構了名為 DownloadService 的服務,且該服務設計為從網路下載檔案,則可以使用下列程式碼啟動服務:

Kotlin

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
val downloadIntent = Intent(this, DownloadService::class.java).apply {
    data = Uri.parse(fileUrl)
}
startService(downloadIntent)

Java

// 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(Context, Class) 建構函式會提供應用程式 Context,以及元件 Class 物件。因此,此意圖會明確啟動應用程式中的 DownloadService 類別。

如要進一步瞭解如何建構及啟動服務,請參閱服務指南。

隱含意圖範例

隱含意圖會指定動作,可以在裝置上叫用任何可執行該動作的應用程式。如果您的應用程式無法執行動作,但其他應用程式可能可以這樣做,而您希望使用者選擇要使用的應用程式,則使用隱含意圖會很有用。

舉例來說,如果您希望使用者與他人分享內容,請使用 ACTION_SEND 動作建立意圖,並新增額外項目來指定要共用的內容。當您使用該意圖呼叫 startActivity() 時,使用者就能選擇要透過哪個應用程式分享內容。

Kotlin

// Create the text message with a string.
val sendIntent = Intent().apply {
    action = Intent.ACTION_SEND
    putExtra(Intent.EXTRA_TEXT, textMessage)
    type = "text/plain"
}

// Try to invoke the intent.
try {
    startActivity(sendIntent)
} catch (e: ActivityNotFoundException) {
    // Define what your app should do if no activity can handle the intent.
}

Java

// 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");

// Try to invoke the intent.
try {
    startActivity(sendIntent);
} catch (ActivityNotFoundException e) {
    // Define what your app should do if no activity can handle the intent.
}

呼叫 startActivity() 時,系統會檢查所有已安裝的應用程式,判斷哪些應用程式可以處理這種意圖 (具有 ACTION_SEND 動作的意圖,以及包含「文字/純文字」資料的意圖)。如果只有一個應用程式能處理應用程式,該應用程式會立即開啟,並提供該意圖。如果其他應用程式無法處理該問題,應用程式可擷取會發生的 ActivityNotFoundException。如有多項活動接受意圖,系統會顯示對話方塊 (如圖 2 所示),讓使用者可以挑選要使用的應用程式。

如要進一步瞭解如何啟動其他應用程式,請參閱「將使用者傳送至其他應用程式」相關指南。

圖 2. 選擇工具對話方塊。

強制執行應用程式選擇工具

如有多個應用程式可回應您的隱含意圖,使用者便可選取要使用的應用程式,並將該應用程式設為動作的預設選擇。在執行使用者可能想一次使用相同應用程式的動作時 (例如開啟網頁時,使用者通常只偏好使用一個網路瀏覽器),選取預設選項就能派上用場。

但是,如果多個應用程式可以回應該意圖,使用者也可能想每次都使用不同的應用程式,則應明確顯示選擇工具對話方塊。選擇器對話方塊會要求使用者選取要用於該動作的應用程式 (使用者無法選取動作的預設應用程式)。舉例來說,當應用程式使用 ACTION_SEND 動作執行「分享」時,使用者可能會想根據他們目前的狀況透過其他應用程式共用內容,因此建議您一律使用選擇器對話方塊,如圖 2 所示。

如要顯示選擇器,請使用 createChooser() 建立 Intent 並傳遞至 startActivity(),如以下範例所示。這個範例顯示的應用程式清單會回應傳送至 createChooser() 方法的意圖,並使用提供的文字做為對話方塊標題。

Kotlin

val sendIntent = Intent(Intent.ACTION_SEND)
...

// Always use string resources for UI text.
// This says something like "Share this photo with"
val title: String = resources.getString(R.string.chooser_title)
// Create intent to show the chooser dialog
val chooser: Intent = Intent.createChooser(sendIntent, title)

// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(packageManager) != null) {
    startActivity(chooser)
}

Java

Intent sendIntent = new Intent(Intent.ACTION_SEND);
...

// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show the chooser dialog
Intent chooser = Intent.createChooser(sendIntent, title);

// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}

偵測不安全的意圖啟動作業

您的應用程式可能會啟動意圖,以瀏覽應用程式內的元件,或代表其他應用程式執行動作。為了提昇平台安全性,Android 12 (API 級別 31) 以上版本會提供偵錯功能,當您應用程式以不安全的方式啟動意圖時,系統會發出警告。舉例來說,您的應用程式可能會執行「巢狀意圖」以不安全的方式啟動;巢狀意圖是一種以額外意圖形式傳遞的意圖,

如果應用程式執行下列這兩項動作,系統會偵測到不安全的意圖啟動作業,並發生 StrictMode 違規事件:

  1. 應用程式將巢狀意圖從已傳遞意圖的額外項目中拆解出來。
  2. 應用程式立即使用該巢狀意圖啟動某個應用程式元件,例如將意圖傳遞至 startActivity()startService()bindService()

如要進一步瞭解如何找出這種情況並修改應用程式,請參閱 Medium 有關「Android Nesting Intents」的網誌文章。

檢查不安全的意圖啟動項目

如要檢查應用程式是否啟動不安全的意圖,請在設定 VmPolicy 時呼叫 detectUnsafeIntentLaunch(),如以下程式碼片段所示。如果您的應用程式偵測到 StrictMode 違規事件,建議您停止應用程式執行作業,保護可能的機密資訊。

Kotlin

fun onCreate() {
    StrictMode.setVmPolicy(VmPolicy.Builder()
        // Other StrictMode checks that you've previously added.
        // ...
        .detectUnsafeIntentLaunch()
        .penaltyLog()
        // Consider also adding penaltyDeath()
        .build())
}

Java

protected void onCreate() {
    StrictMode.setVmPolicy(new VmPolicy.Builder()
        // Other StrictMode checks that you've previously added.
        // ...
        .detectUnsafeIntentLaunch()
        .penaltyLog()
        // Consider also adding penaltyDeath()
        .build());
}

以更負責任的態度使用意圖

為了盡可能降低不安全的意圖啟動及違反 StrictMode 問題的機率,請遵循下列最佳做法。

僅複製意圖中的必要額外項目,並執行任何必要的清理和驗證作業。應用程式可能會將額外項目從某個意圖複製到另一個用於啟動新元件的意圖。當應用程式呼叫 putExtras(Intent)putExtras(Bundle) 時,就會發生這種情況。如果您的應用程式執行其中一項作業,請只複製接收元件預期的額外項目。如果其他意圖 (接收副本) 會啟動未匯出的元件,請先清理並驗證額外內容,再將這些額外內容複製到啟動元件的意圖。

請勿視需要匯出應用程式元件。舉例來說,如果您想使用內部巢狀結構意圖啟動應用程式元件,請將該元件的 android:exported 屬性設為 false

使用 PendingIntent 而非巢狀意圖。這樣一來,當其他應用程式拆解包含 IntentPendingIntent 時,其他應用程式就能使用應用程式的身分啟動 PendingIntent。這項設定可讓其他應用程式安全地啟動您應用程式中的任何元件,包括未匯出的元件。

圖 2 中的圖表顯示如何將控制權從 (用戶端) 應用程式傳遞至另一個 (服務) 應用程式,再傳回應用程式:

  1. 應用程式會建立意圖來叫用其他應用程式的活動,並在該意圖中新增 PendingIntent 物件做為額外項目。這個待處理意圖會叫用應用程式中的元件;系統不會匯出這個元件。
  2. 收到應用程式的意圖後,其他應用程式會擷取巢狀 PendingIntent 物件。
  3. 其他應用程式會在 PendingIntent 物件上叫用 send() 方法。
  4. 將控制項傳回應用程式後,系統就會使用應用程式的內容叫用待處理意圖。

圖 2:使用巢狀待處理意圖時的跨應用程式通訊圖表。

接收隱含意圖

如要通告應用程式可接收的隱含意圖,請使用資訊清單檔案中的 <intent-filter> 元素,為每個應用程式元件宣告一或多個意圖篩選器。每個意圖篩選器都會根據意圖的動作、資料和類別,指定可接受的意圖類型。只有在意圖可通過其中一個意圖篩選器時,系統才會向應用程式元件傳送隱含意圖。

注意:無論元件宣告的意圖篩選器為何,系統一律會將明確意圖傳遞至目標。

應用程式元件應針對每個可執行的工作分別宣告不同的篩選器。舉例來說,圖片庫應用程式中的一項活動可能會有兩個篩選器:一個用於檢視圖片的篩選器,另一個則用於編輯圖片。活動啟動時,系統會檢查 Intent,並根據 Intent 中的資訊決定行為方式 (例如是否顯示編輯器控制項)。

每個意圖篩選器都是由應用程式資訊清單檔案中的 <intent-filter> 元素定義,該元素是以巢狀結構的形式置於對應的應用程式元件中 (例如 <activity> 元素)。

在每個包含 <intent-filter> 元素的應用程式元件中,明確設定 android:exported 的值。這項屬性表示其他應用程式是否可存取應用程式元件。在某些情況下 (例如,意圖篩選器包含 LAUNCHER 類別的活動),建議將這項屬性設為 true。否則,建議您將這項屬性設為 false,較為安全。

警告:如果應用程式中的活動、服務或廣播接收器使用意圖篩選器,且未明確設定 android:exported 的值,就無法在搭載 Android 12 以上版本的裝置上安裝應用程式。

<intent-filter> 中,您可以使用下列三個元素中的一或多個元素,指定要接受的意圖類型:

<action>
name 屬性中宣告已接受的意圖動作。這個值必須是動作的常值字串值,而非類別常數。
<data>
使用一或多個屬性指定資料 URI 的各個面向 (schemehostportpath) 和 MIME 類型,然後宣告接受的資料類型。
<category>
name 屬性中宣告可接受的意圖類別。這個值必須是動作的常值字串值,而非類別常數。

注意:如要接收隱含意圖,您必須在意圖篩選器中加入 CATEGORY_DEFAULT 類別。startActivity()startActivityForResult() 方法會將所有意圖視為皆已宣告 CATEGORY_DEFAULT 類別。如果您未在意圖篩選器中宣告此類別,系統就不會將任何隱含意圖解析至您的活動。

舉例來說,以下活動宣告,其意圖篩選器可在資料類型為文字時接收 ACTION_SEND 意圖:

<activity android:name="ShareActivity" android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

您可以建立包含多個 <action><data><category> 例項的篩選器。如要這麼做,請務必確認該元件能夠處理這些篩選器元素的任何及所有組合。

如要處理多種意圖,但只想在特定動作、資料和類別類型組合中,則需要建立多個意圖篩選器。

系統會比較這三個元素的意圖,對照篩選器來測試隱含意圖。意圖必須通過全部三項測試,要傳遞至元件。如果只與其中一個元件配對失敗,Android 系統就不會將意圖提供給元件。不過,由於一個元件可能有多個意圖篩選器,因此未通過某個元件的篩選器的意圖可能會通過另一個篩選器。如要進一步瞭解系統如何解析意圖,請參閱下方的意圖解決方案一節。

注意: 如要防止其他應用程式啟動元件,使用意圖篩選器並非安全做法。雖然意圖篩選器會限制元件只能回應特定類型的隱含意圖,但如果開發人員確定您的元件名稱,其他應用程式也可使用明確意圖啟動應用程式元件。如果只有您的應用程式必須能夠啟動其中一個元件,請勿在資訊清單中宣告意圖篩選器,請改為將該元件的 exported 屬性設為 "false"

同樣地,為避免意外執行其他應用程式的 Service,請一律使用明確意圖來啟動自己的服務。

注意:對於所有活動,您必須在資訊清單檔案中宣告意圖篩選器。不過,您可以呼叫 registerReceiver() 來動態註冊廣播接收器的篩選器。接著,您可以使用 unregisterReceiver() 取消註冊接收器。如此一來,應用程式就能讓應用程式僅在指定時間範圍內監聽特定廣播訊息。

篩選器範例

為了示範部分意圖篩選器行為,以下舉例來自社交分享應用程式的資訊清單檔案:

<activity android:name="MainActivity" android:exported="true">
    <!-- This activity is the main entry, should appear in app launcher -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity android:name="ShareActivity" android:exported="false">
    <!-- This activity handles "SEND" actions with text data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
    <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <action android:name="android.intent.action.SEND_MULTIPLE"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="application/vnd.google.panorama360+jpg"/>
        <data android:mimeType="image/*"/>
        <data android:mimeType="video/*"/>
    </intent-filter>
</activity>

第一個活動 MainActivity 是應用程式的主要進入點,也就是使用者一開始透過啟動器圖示啟動應用程式時開啟的活動:

  • ACTION_MAIN 動作表示這是主要進入點,且不會預期任何意圖資料。
  • CATEGORY_LAUNCHER 類別表示這個活動的圖示應放置在系統的應用程式啟動器中。如果 <activity> 元素未指定帶有 icon 的圖示,系統就會使用 <application> 元素的圖示。

這兩者必須配對,活動才會顯示在應用程式啟動器中。

第二個活動 ShareActivity 旨在促進分享文字和媒體內容。雖然使用者可能會透過 MainActivity 前往該活動,但也可以從另一個應用程式直接輸入 ShareActivity,其會發出符合兩個意圖篩選器的隱含意圖。

注意:MIME 類型 application/vnd.google.panorama360+jpg 是一種特殊資料類型,可以指定全景相片,您可透過 Google 全景 API 處理。

將意圖與其他應用程式的意圖篩選器進行比對

如果其他應用程式指定 Android 13 (API 級別 33) 以上版本為目標,只有在您的意圖與其他應用程式中 <intent-filter> 元素的動作和類別相符時,應用程式才能處理應用程式的意圖。如果系統找不到相符項目,就會擲回 ActivityNotFoundException。傳送應用程式必須處理這個例外狀況。

同樣地,如果您將應用程式更新為指定 Android 13 或以上版本,則來自外部應用程式的所有意圖,只有在該意圖符合應用程式所宣告 <intent-filter> 元素的動作和類別時,才會傳送至應用程式的匯出元件。無論傳送應用程式的目標 SDK 版本為何,都會發生此行為。

在下列情況下,系統不會強制執行意圖比對:

  • 意圖傳送至未宣告任何意圖篩選器的元件。
  • 意圖源自同一應用程式的意圖。
  • 來自系統的意圖,也就是從「系統 UID」(uid=1000) 傳送的意圖。系統應用程式包含 system_server,以及將 android:sharedUserId 設為 android.uid.system 的應用程式。
  • 來自根的意圖。

進一步瞭解意圖比對

使用待處理意圖

PendingIntent 物件是 Intent 物件的包裝函式。PendingIntent 的主要用途是向外國應用程式授予使用內含的 Intent 的權限,就像從應用程式本身的程序執行一樣。

待處理意圖的主要用途包括:

就像每個 Intent 物件的設計都是由特定類型的應用程式元件 (ActivityServiceBroadcastReceiver) 處理,因此也必須以相同的考量來建立 PendingIntent。使用待處理意圖時,應用程式不會透過 startActivity() 等呼叫執行意圖。相反地,您必須在建立 PendingIntent 時呼叫相應的建立者方法,宣告預期的元件類型:

除非您的應用程式從其他應用程式「接收」待處理意圖,否則您需要使用上述方法建立 PendingIntent 才是您需要的 PendingIntent 方法。

每個方法都會使用目前的應用程式 Context、您要包裝的 Intent,以及一或多個指定意圖使用方式的標記 (例如是否可多次使用意圖)。

如要進一步瞭解如何使用待處理意圖,請參閱個別用途的說明文件,例如「通知」和「應用程式小工具」API 指南。

指定可變動性

如果應用程式指定 Android 12 以上版本,您必須為應用程式建立的個別 PendingIntent 物件指定可變動性。如要宣告特定 PendingIntent 物件可變動或不可變動,請分別使用 PendingIntent.FLAG_MUTABLEPendingIntent.FLAG_IMMUTABLE 旗標。

如果應用程式嘗試建立 PendingIntent 物件,但未設定可變動性標記,系統會擲回 IllegalArgumentException,且 Logcat 中也會顯示以下訊息:

PACKAGE_NAME: Targeting S+ (version 31 and above) requires that one of \
FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.

Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if \
some functionality depends on the PendingIntent being mutable, e.g. if \
it needs to be used with inline replies or bubbles.

盡可能建立不可變更的待處理意圖

在大多數情況下,應用程式應建立不可變動的 PendingIntent 物件,如以下程式碼片段所示。如果 PendingIntent 物件不可變更,其他應用程式就無法修改意圖,調整叫用意圖的結果。

Kotlin

val pendingIntent = PendingIntent.getActivity(applicationContext,
        REQUEST_CODE, intent,
        /* flags */ PendingIntent.FLAG_IMMUTABLE)

Java

PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),
        REQUEST_CODE, intent,
        /* flags */ PendingIntent.FLAG_IMMUTABLE);

不過,某些用途會改為需要可變動的 PendingIntent 物件:

  • 支援在通知中進行直接回覆操作。如要直接回覆,則需要變更與回覆相關聯的 PendingIntent 物件中的剪輯資料。您通常需要將 FILL_IN_CLIP_DATA 做為旗標傳遞至 fillIn() 方法,以要求這項變更。
  • 使用 CarAppExtender 例項將通知與 Android Auto 架構建立關聯。
  • 使用 PendingIntent 的執行個體將對話放入對話框。可變動的 PendingIntent 物件可讓系統套用正確的標記,例如 FLAG_ACTIVITY_MULTIPLE_TASKFLAG_ACTIVITY_NEW_DOCUMENT
  • 呼叫 requestLocationUpdates() 或類似的 API,要求裝置位置資訊。可變動的 PendingIntent 物件可讓系統新增代表位置生命週期事件的意圖額外項目。這些事件包括位置變更以及有提供供應器。
  • 使用 AlarmManager 設定鬧鐘。可變動 PendingIntent 物件可讓系統新增 EXTRA_ALARM_COUNT 意圖額外項目。這個額外項目代表週期性鬧鐘觸發的次數,透過包含這項額外項目,意圖就能準確通知應用程式,瞭解使用者是否多次觸發週期性鬧鐘,例如裝置睡覺的時間。

如果應用程式會建立可變動的 PendingIntent 物件,強烈建議您使用明確意圖並填入 ComponentName。這樣一來,每當另一個應用程式叫用 PendingIntent 並將控制項傳回應用程式時,應用程式中的相同元件一律會啟動。

在待處理意圖中使用明確意圖

如要更妥善定義其他應用程式使用應用程式待處理意圖的方式,請一律將待處理意圖包裝在明確意圖周圍。為協助遵循這項最佳做法,請執行下列步驟:

  1. 確認已設定基本意圖的動作、套件和元件欄位。
  2. 使用 Android 6.0 (API 級別 23) 中新增的 FLAG_IMMUTABLE 建立待處理意圖。此標記可防止收到 PendingIntent 的應用程式填入未填入的屬性。如果應用程式的 minSdkVersion22 以下,可以使用下列程式碼同時提供安全和相容性:

    if (Build.VERSION.SDK_INT >= 23) {
      // Create a PendingIntent using FLAG_IMMUTABLE.
    } else {
      // Existing code that creates a PendingIntent.
    }

意圖解析

當系統收到啟動活動的隱含意圖時,系統會根據以下三個面向,將該活動與意圖篩選器進行比較,藉此搜尋最適合該意圖的活動:

  • 動作。
  • 資料 (URI 和資料類型)。
  • 類別。

以下各節將說明系統如何根據應用程式資訊清單檔案中的意圖篩選器宣告,將意圖與適當的元件進行比對。

動作測試

如要指定可接受的意圖動作,意圖篩選器可以宣告零或多個 <action> 元素,如以下範例所示:

<intent-filter>
    <action android:name="android.intent.action.EDIT" />
    <action android:name="android.intent.action.VIEW" />
    ...
</intent-filter>

如要傳遞這個篩選器,Intent 中指定的動作必須與篩選器中列出的其中一個動作相符。

如果篩選器並未列出任何動作,就沒有任何意圖符合比對的情況,因此所有意圖都會失敗測試。不過,如果 Intent 未指定動作,只要篩選器包含至少一個動作,測試就會通過。

類別測試

如要指定可接受的意圖類別,意圖篩選器可以宣告零或多個 <category> 元素,如以下範例所示:

<intent-filter>
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    ...
</intent-filter>

如要通過類別測試,Intent 中的每個類別都必須與篩選器中的類別相符。反之,意圖篩選器可能會宣告超過 Intent 中指定的類別,且 Intent 仍會傳遞。因此,無論篩選器中宣告了哪些類別,沒有類別的意圖一律會通過這項測試。

注意:Android 會自動將 CATEGORY_DEFAULT 類別套用至傳送至 startActivity()startActivityForResult() 的所有隱含意圖。如要讓活動接收隱含意圖,就必須在意圖篩選器中加入 "android.intent.category.DEFAULT" 的類別,如先前的 <intent-filter> 範例所示。

資料測試

如要指定可接受的意圖資料,意圖篩選器可以宣告零或多個 <data> 元素,如以下範例所示:

<intent-filter>
    <data android:mimeType="video/mpeg" android:scheme="http" ... />
    <data android:mimeType="audio/mpeg" android:scheme="http" ... />
    ...
</intent-filter>

每個 <data> 元素都可以指定 URI 結構和資料類型 (MIME 媒體類型)。URI 的每個部分都是獨立的屬性:schemehostportpath

<scheme>://<host>:<port>/<path>

這些屬性可能的值如下:

content://com.example.project:200/folder/subfolder/etc

在這個 URI 中,配置為 content、主機為 com.example.project、通訊埠 200,路徑為 folder/subfolder/etc

這些屬性在 <data> 元素中都是選用項目,但具有線性依附元件:

  • 如果未指定配置,系統會忽略主機。
  • 如果未指定主機,系統會忽略通訊埠。
  • 如果未指定配置和主機,路徑將遭到忽略。

將意圖中的 URI 與篩選器中的 URI 規格進行比較時,只會與篩選器中包含的 URI 部分進行比較。例如:

  • 如果篩選器僅指定配置,則所有具備該配置的 URI 都會符合篩選器。
  • 如果篩選器指定配置和授權,但沒有路徑,則所有具有相同配置和授權的 URI 都會通過篩選器,不論其路徑為何。
  • 如果篩選器指定了配置、授權和路徑,則只有具備相同配置、主機名稱和路徑的 URI 才會通過篩選器。

注意:路徑規格可以包含萬用字元星號 (*),只要求與路徑名稱進行部分比對。

資料測試會將意圖的 URI 和 MIME 類型與篩選器中指定的 URI 和 MIME 類型進行比較。規則如下:

  1. 只有在篩選器未指定任何 URI 或 MIME 類型時,不含 URI 或 MIME 類型的意圖才會通過測試。
  2. 包含 URI 但沒有 MIME 類型的意圖 (無論是明確或從 URI 推論) 時,只有當其 URI 符合篩選器的 URI 格式,且篩選器未指定 MIME 類型時,系統才會通過測試。
  3. 只有當篩選器列出相同的 MIME 類型且未指定 URI 格式時,包含 MIME 類型但未指定 URI 的意圖才會通過測試。
  4. 同時包含 URI 和 MIME 類型的意圖 (不論是明確或從 URI 可推論) 時,只有當該類型與篩選器中列出的類型相符時,系統才會傳送測試的 MIME 類型部分。如果 URI 與篩選器中的 URI 相符,或有 content:file: URI 且篩選器未指定 URI,則會傳遞測試的 URI 部分。也就是說,如果元件的篩選清單「只列出」MIME 類型清單,系統會假設元件支援 content:file: 資料。

注意:如果意圖指定 URI 或 MIME 類型,如果 <intent-filter> 中沒有 <data> 元素,資料測試就會失敗。

最後一項規則 (d) 反映了元件可從檔案或內容供應器取得本機資料的預期。因此,篩選器可以只列出資料類型,不必明確命名 content:file: 配置。以下範例是 <data> 元素向 Android 說明元件可從內容供應器取得並顯示圖片資料的常見情況:

<intent-filter>
    <data android:mimeType="image/*" />
    ...
</intent-filter>

最常見的篩選器是指定資料類型,但不指定 URI,因為大部分可用資料都是由內容供應器供應。

另一個常見的設定是包含配置和資料類型的篩選器。例如,如下所示的 <data> 元素會指示 Android 元件可從網路擷取影片資料,以便執行動作:

<intent-filter>
    <data android:scheme="http" android:mimeType="video/*" />
    ...
</intent-filter>

意圖比對

系統會將意圖和意圖篩選器進行比對,藉此找出要啟用的目標元件,也會探索裝置上的元件組合相關資訊。舉例來說,Google Home 應用程式會使用指定 ACTION_MAIN 動作和 CATEGORY_LAUNCHER 類別的意圖篩選器尋找所有活動,藉此填入應用程式啟動器。只有在意圖中的動作和類別與篩選器相符時 (如 IntentFilter 類別的說明文件所述),才會成功比對。

應用程式可以透過與 Google Home 應用程式類似的方式使用意圖比對功能。PackageManager 具有一組 query...() 方法,可傳回可接受特定意圖的所有元件,以及一系列類似的 resolve...() 方法,用於決定回應意圖的最佳元件。舉例來說,queryIntentActivities() 會傳回可執行意圖以引數形式傳遞的所有活動清單,而 queryIntentServices() 會傳回類似的服務清單。這兩種方法都不會啟用元件,只會列出可回應的元件。您還可以使用類似的方法 queryBroadcastReceivers() 來處理廣播接收器。