意圖和意圖篩選器

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(),即可將廣播訊息傳遞給其他應用程式。

本頁其餘部分將說明意圖的運作方式及使用方法。詳情請參閱「與其他應用程式互動」和「分享內容」。

意圖類型

意圖有兩種類型:

  • 明確意圖:指定完整的 ComponentName,指定應用程式滿足意圖的元件。由於您知道要啟動的活動或服務類別名稱,因此您通常會使用明確意圖,在自己的應用程式中啟動元件。舉例來說,您可能會為了回應使用者操作,在應用程式中啟動新活動,或啟動服務在背景下載檔案。
  • 「隱含意圖」不會提及特定元件,而是宣告要執行的一般動作,讓其他應用程式的元件能夠處理該元件。舉例來說,如果您想在地圖上向使用者顯示地點,可以使用隱含意圖,要求另一個能夠在地圖上顯示特定位置的應用程式。

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

圖 1 隱含意圖如何透過系統傳送,以啟動其他活動:[1] Activity A 會建立包含動作說明的 Intent,並傳遞至 startActivity()[2] Android 系統會搜尋所有應用程式,找出符合意圖的意圖篩選器。找到相符項目時,[3] 系統會叫用其 onCreate() 方法並傳遞 Intent,來啟動相符的活動 (Activity 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";
資料
URI (Uri 物件) 參照要對該資料執行操作的資料和/或 MIME 類型。提供的資料類型通常會取決於意圖的動作。舉例來說,如果動作是 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 巢狀意圖的網誌文章。

檢查是否有不安全的意圖啟動作業

如要檢查應用程式中是否有不安全的意圖啟動作業,請在設定 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()