intent 和 intent 过滤器

Intent 是一种消息传递对象,可用于请求操作 从另一个应用组件复制而来。 尽管 intent 可通过多种方式促进组件之间的通信, 基本用例:

  • 启动 activity

    Activity 表示应用中的单个屏幕。您可以发起新的 通过传递 Intent 来调用 Activity 的实例 发送至 startActivity()Intent 描述了要启动的 activity,并携带了任何必要的数据。

    如果您想在 activity 完成时收到结果, 调用 startActivityForResult()。您的 activity 会收到结果 在 activity 的 onActivityResult() 回调中设置为单独的 Intent 对象。 如需了解详情,请参阅 activity 指南。

  • 启动服务

    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()

本页的其余部分介绍 intent 的工作原理和使用方法。 如需了解相关信息,请参阅 与其他应用交互分享内容

Intent 类型

Intent 分为两种类型:

  • 显式 intent 通过指定完整的 ComponentName 来指定可满足 intent 的应用的哪个组件。您 通常使用显式 intent 来启动 您自己的应用,因为您知道要启动的 activity 或服务的类名称。对于 例如,您可以在应用中启动新 activity 以响应用户操作,或启动 用于在后台下载文件的服务。
  • 隐式 intent 不会指定特定组件的名称,而是声明常规操作 以便其他应用中的组件来处理它。例如,如果您希望 在地图上向用户显示位置,则可以使用隐式 Intent 请求另一个具有 应用在地图上显示指定位置。

图 1 显示了启动 activity 时如何使用 intent。当 Intent 对象会明确命名特定的 activity 组件,系统会 立即启动该组件

图 1. 隐式 intent 启动另一个 activity:[1] activity A 会创建一个 Intent,包含操作说明,并将其传递给 startActivity()[2] Android 系统会搜索所有 应用。找到匹配后,[3] 系统 通过调用匹配 activity (activity B) 的 onCreate() 方法并向其传递 Intent 来启动该 activity。

使用隐式 intent 时,Android 系统会查找相应的组件来启动 将 intent 的内容与设备上其他应用的清单文件中声明的 intent 过滤器进行比较, 设备。如果 intent 与 intent 过滤器匹配,则系统将启动该组件并传递 Intent 对象。如果多个 intent 过滤器兼容,系统会 显示一个对话框,以便用户选择要使用的应用。

intent 过滤器是应用清单文件中的一个表达式, 用于指定组件要执行的 intent 的类型 希望收到的内容。例如,通过为 activity 声明 intent 过滤器, 您可以让其他应用通过某种 intent 直接启动您的 activity。 同样,如果您没有为 activity 声明任何 intent 过滤器,则可以启动该 activity 只有在显式 intent 中才能实现。

注意:为确保您的应用安全无虞,请务必 使用显式 intent 启动 Service 为您的服务声明 intent 过滤器。使用隐式 intent 启动服务存在安全隐患,因为您无法确定哪些服务将响应 intent,且用户无法看到哪些服务已启动。从 Android 5.0(API 级别 21)开始,系统 如果调用 bindService(),则会抛出异常 具有隐式 intent。

构建 Intent

Intent 对象携带了 Android 系统使用的信息 确定要从哪个组件启动(例如,确切的组件名称或组件 类别),以及接收方组件在 以便正确执行操作(例如要执行的操作以及要操作的数据)。

Intent 中包含的主要信息如下:

组件名称
要启动的组件的名称。

这是可选操作,但对于用户来说,这是构成用户意图的关键 显式,这意味着 intent 应仅传递给应用组件 由组件名称定义如果没有组件名称,则 intent 为隐式,且 系统根据其他 intent 信息决定哪个组件应接收 intent (如下文所述的操作、数据和类别)。如果您需要启动特定的 您应指定组件名称。

注意:启动 Service 时, 请务必指定组件名称。否则,您无法确定 将响应该 intent,且用户无法看到哪项服务已启动。

Intent 的这一字段是 ComponentName 对象,您可以使用完全限定的 目标组件的限定类名称,包括应用的软件包名称,例如 com.example.ExampleActivity。您可以使用 setComponent()setClass()setClassName()、 或者使用 Intent 构造函数。

操作
一个字符串,用于指定要执行的常规操作(例如“查看”或“选择”)。

对于广播 intent,这是指已发生且正在报告的操作。 该操作在很大程度上决定了其余 intent 的结构,特别是 intent 数据和 extra 中包含的信息。

您可以指定自己的操作供 intent 在您的应用中使用(或者供其他 intent 使用) 应用来调用应用中的组件),但您通常需要指定操作常量 由 Intent 类或其他框架类定义。以下是一些 启动 activity 的常见操作:

ACTION_VIEW
当您获得一些信息时,请在 intent 中使用 startActivity() 来执行此操作 可以向用户显示的 activity,例如要在图库应用中查看的照片或 地图应用中的视图。
ACTION_SEND
也称为“share” intent。如果您拥有一些可供用户获取的数据,则应将 intent 与 startActivity() 搭配使用 通过其他应用(例如电子邮件应用或社交分享应用)进行分享。

如需了解详情,请参阅 Intent 类参考文档 常量用于定义通用操作已指定其他操作 Android 框架中的其他位置,例如 Settings 中用于表示操作 可在系统的“设置”应用中打开特定屏幕。

您可以使用 setAction()Intent 构造函数为 intent 指定操作。

如果您定义自己的操作,请务必添加应用的软件包名称 用作前缀,如以下示例所示:

Kotlin

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

Java

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
数据
引用数据的 URI(Uri 对象) 和/或 该数据的 MIME 类型。提供的数据类型通常由 intent 的操作决定。对于 例如,如果操作为 ACTION_EDIT,则数据应包含 要修改的文档的 URI。

创建 intent 时, 除 URI 之外,指定数据类型(其 MIME 类型)通常也很重要。 例如,能显示图片的 activity 可能无法 即使 URI 格式可能类似,也不例外。 指定数据的 MIME 类型有助于 Android 系统会查找接收 intent 的最佳组件。 不过,MIME 类型有时可以根据 URI 推断出来,尤其是当数据 content: URI。content: URI 指示数据位于设备上 由 ContentProvider:可使数据 MIME 类型对系统可见。

如需仅设置数据 URI,请调用 setData()。 如需仅设置 MIME 类型,请调用 setType()。如有必要, 可以使用 setDataAndType() 明确设置这两项。

注意:如果您要同时设置 URI 和 MIME 类型, 不要调用 setData()setType(),因为它们会让彼此的值失效。 始终使用 setDataAndType() 同时设置 URI 和 MIME 类型。

类别
一个字符串,其中包含有关组件种类的附加信息 处理 intent 的应用。您可以输入任意数量的类别描述, 放置在 intent 中,但大多数 intent 不需要类别。 以下是一些常见类别: <ph type="x-smartling-placeholder">
</ph>
CATEGORY_BROWSABLE
目标 activity 允许本身由网络浏览器启动以显示数据 通过图片或电子邮件等链接引用的应用。
CATEGORY_LAUNCHER
activity 是任务的初始 activity,并列在 系统的应用启动器。

如需查看完整的 API 列表,请参阅 Intent 类说明。 类别。

您可以使用 addCategory() 指定类别。

上面列出的这些属性(组件名称、操作、数据和类别)代表 intent 的特征。通过读取这些属性,Android 系统会 能够解析应启动哪个应用组件。不过,intent 可以 对现有代码没有影响的 解析为应用组件的方式Intent 还可以提供以下信息:

Extra
包含完成操作所需的额外信息的键值对 请求的操作。 正如某些操作使用特定类型的数据 URI 一样,有些操作也使用特定的 extra。

您可以使用各种 putExtra() 方法添加额外数据, 每个对象都接受两个形参:键名和值。 您还可以创建一个包含所有额外数据的 Bundle 对象,然后将其插入 将 Intent 中的 Bundle 替换为 putExtras()

例如,在创建意图以发送 ACTION_SEND,则可以使用 EXTRA_EMAIL 键,并使用 EXTRA_SUBJECT 键。

Intent 类会指定多个 EXTRA_* 常量 用于标准化数据类型。如果您需要声明自己的 extra 键(对于 应用收到的内容),请务必加入应用的软件包名称 用作前缀,如以下示例所示:

Kotlin

const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"

Java

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

注意:请勿使用 ParcelableSerializable 数据(在发送您预期的 intent 时) 另一个应用来接收内容如果应用 它尝试访问 Bundle 对象中的数据,但未 有权访问 Parceled 或序列化类,则系统会引发 RuntimeException

标志
标志在 Intent 类中定义,用作 intent。这些标志可以指示 Android 系统如何启动 activity(例如, 任务 该 activity 应该属于哪个 activity 以及启动后如何处理(例如,是否属于 活动)。

如需了解详情,请参阅 setFlags() 方法。

显式 Intent 示例

显式 intent 是指用于启动特定应用组件的 intent,如 特定 activity 或服务。如需创建显式 intent,请定义 Intent 对象的组件名称 - all 其他 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 对象。因此 此 intent 会显式启动应用中的 DownloadService 类。

如需详细了解如何构建和启动服务,请参阅 服务指南。

隐式 Intent 示例

隐式 intent 用于指定可调用能调用设备上的任何应用的操作 执行操作如果您的应用无法执行 操作,但其他应用可能可以,并且您希望用户选择要使用的应用。

例如,如果您希望用户与其他人分享您的内容, 创建 intent 包含 ACTION_SEND 操作 并添加用于指定要分享内容的 extras 参数。致电后 startActivity() 与该 intent 相关联,则用户可以 选择用来分享内容的应用。

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() 时,系统会 检查所有已安装的应用,确定哪些应用能够处理此类 intent( intent 包含 ACTION_SEND 操作,并且带有“text/plain” 数据)。如果只有一个应用能够处理,该应用会立即打开并显示 intent。如果其他应用无法处理,您的应用可以捕获 ActivityNotFoundException 情况。如果多个 activity 接受 intent,系统会 将显示如图 2 所示的对话框,以便用户选择要使用的应用。

该指南中还提供了有关如何启动其他应用的更多信息 将用户引导至 另一个应用

图 2. 选择器对话框。

强制使用应用选择器

如果有多个应用响应隐式 intent, 用户可以选择要使用哪个应用,并将该应用设为 操作。在执行用户要完成的操作时,选择默认值的功能非常有用 可能希望每次都使用同一应用,例如在打开网页(用户 通常只使用一种网络浏览器)。

但是,如果多个应用可以响应 Intent,并且用户可能希望使用不同的 Intent,则 应用,您应明确显示选择器对话框。选择器对话框会询问 用户选择要用于操作的应用(用户无法选择默认应用) 操作)。例如,当应用执行“分享”操作时ACTION_SEND 操作触发后,用户可能希望使用其他应用进行分享,具体取决于 因此您应始终使用选择器对话框,如图 2 所示。

如需显示选择器,请使用 createChooser() 创建 Intent 并将其传递给 startActivity(),如以下示例所示。 此示例会显示一个对话框,其中包含可响应传递给 createChooser() 方法的 intent 的应用列表,并将提供的文本用作 对话框标题。

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);
}

检测不安全的 intent 启动

您的应用可能会启动 intent,以便在应用内的各个组件之间导航, 或代表其他应用执行操作。为了提高平台安全性, Android 12(API 级别 31)及更高版本提供调试功能,可向您发出警告 您的应用以不安全的方式启动 intent。例如,您的应用可能会 以不安全的方式启动嵌套 intent,此 intent 是指 另一个 intent 中的 extra。

如果您的应用同时执行以下两项操作,系统会检测到不安全的 intent 启动,并且违反了 StrictMode 政策 发生:

  1. 您的应用从已传递的 intent 的 extra 中解封嵌套 intent。
  2. 您的应用立即使用该嵌套 intent 启动应用组件,例如将 intent 传递给 startActivity()startService()bindService()

如需详细了解如何识别这种情况并更改您的应用, 阅读有关Android Nesting 的博客文章 intent 来自 Medium。

检查是否存在不安全的 intent 启动

如需检查您的应用中是否有不安全的 intent 启动,请在配置 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());
}

更负责地使用 intent

为了最大限度地降低不安全 intent 启动和 StrictMode 违规行为发生的可能性, 请遵循以下最佳做法。

仅复制 intent 中的必要 extra,并执行任何必要的清理和验证。您的应用可能会将 extra 从一个 intent 复制到用于启动新组件的另一个 intent。当您的应用调用 putExtras(Intent)putExtras(Bundle) 时,就会发生这种情况。如果您的应用执行这些操作其中之一,请仅复制接收组件所期望的 extra。如果另一个 intent(它接收副本)启动一个未导出的组件,请先清理和验证 extra,然后再将其复制到启动该组件的 intent。

请勿不必要地导出应用的组件。例如,如果您 打算使用内部嵌套 intent 启动应用组件, 将组件的 android:exported 属性设置为 false

使用 PendingIntent,而不是 嵌套 intent。这样,当另一个应用解压缩其 PendingIntent 的 包含 Intent,其他应用可以使用PendingIntent 识别应用的身份此配置可让其他应用安全启动 任何组件,包括未导出的组件。

图 2 中的示意图显示了系统如何从您的(客户端)传递控件 应用复制到另一个(服务)应用,然后再返回给您的应用:

  1. 您的应用会创建一个 intent,用于调用其他应用中的 activity。介于 该 intent 可以添加 PendingIntent 对象作为 extra。此待处理 intent 调用应用中的组件;系统不会导出该组件
  2. 收到应用的 intent 后,其他应用将提取 PendingIntent 对象。
  3. 另一个应用对 PendingIntent 对象调用 send() 方法。
  4. 将控制权传回您的应用后,系统会调用待处理 intent。

图 2.使用嵌套的待处理时的应用间通信示意图 intent。

接收隐式 Intent

如需公布您的应用可以接收哪些隐式 intent,请声明一个或多个 intent 过滤器 每个应用组件都有一个 <intent-filter> 清单文件中. 每个 intent 过滤器都会根据 intent 的操作指定其接受的 intent 类型, 数据和类别仅当隐式 intent 的存在时, intent 可以通过某个 intent 过滤器传递。

注意:显式 intent 始终会传递给其目标, 而不管组件声明了任何 intent 过滤器。

应用组件应为其可执行的每个唯一作业声明单独的过滤器。 例如,图库应用中的一个 activity 可能有两个过滤器:一个过滤器 还可使用另一个过滤条件编辑图片。当 activity 启动时, 它会检查 Intent,并根据提供的信息确定行为方式 在 Intent 中(例如,是否显示编辑器控件)。

每个 intent 过滤器都由 <intent-filter> 定义 元素,并嵌套在相应的应用组件(如 以 <activity> 身份 元素)。

在每个包含 <intent-filter> 元素的应用组件中, 明确设置一个 android:exported。 此属性指示应用组件是否可供其他应用访问。在某些 例如 activity 的 intent 过滤器包含 LAUNCHER 则有必要将此属性设置为 true。否则 将此属性设为 false 更加安全。

警告:如果某项活动、服务或广播 应用中的接收器使用 intent 过滤器,并且未明确设置值 android:exported 时,您的应用将无法安装在符合以下条件的设备上 搭载 Android 12 或更高版本。

<intent-filter> 内, 您可以使用一个或多个 三个要素:

<action>
name 属性中声明接受的 intent 操作。值 必须是操作的文字字符串值,而不是类常量。
<data>
使用一个或多个指定各种属性的属性,声明所接受的数据类型 数据 URI 的各个方面(schemehostportpath)和 MIME 类型。
<category>
name 属性中声明接受的 intent 类别。值 必须是操作的文本字符串值,而不是类常量。

注意:要接收隐式 intent,您需要 必须包含 CATEGORY_DEFAULT 类别。方法 startActivity()startActivityForResult() 处理所有 intent 就像声明了 CATEGORY_DEFAULT 类别一样。 如果您不在 intent 过滤器中声明此类别,则任何隐式 intent 都不会解析为 活动

例如,以下是一个包含 intent 过滤器的 activity 声明,用于接收 ACTION_SEND intent:

<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>。 如果这样做,您需要确保该组件能够处理 过滤元素的组合。

当您想要处理多种 intent,但只以特定的组合形式处理时 操作、数据和类别类型,则需要创建多个 intent 过滤器。

系统通过将 Intent 与每个过滤器进行比较,根据过滤器测试隐式 Intent。 三个元素。若要传递给组件,intent 必须通过全部三项测试。 如果 Intent 与其中任何一项都不匹配,那么 Android 系统不会将 intent 传递给 组件。但是,由于一个组件可能有多个 intent 过滤器,因此执行 不通过某个组件的某个过滤器,则可能会通过另一个过滤器。 如需详细了解系统如何解析 intent,请参阅以下部分 intent 解析部分。

注意 :使用 intent 过滤器无法安全地阻止其他应用启动 组件。尽管 intent 过滤器限制组件仅响应 某些类型的隐式 intent,那么其他应用可能会启动您的应用组件 如果开发者确定您的组件名称,请使用显式 intent。 如果确保只有您自己的应用才能启动您的某个组件, 请勿在清单中声明 intent 过滤器。而是将 exported 属性 "false"

同样,为了避免无意中运行其他应用的 Service,请始终使用显式 intent 来启动您自己的服务。

注意: 对于所有 activity,您都必须在清单文件中声明 intent 过滤器。 不过,广播接收器的过滤器可以通过调用 registerReceiver()。然后,您可以使用 unregisterReceiver() 取消注册该接收器。这样做可让您的应用 仅在特定时间段内监听特定广播 正在运行。

过滤器示例

为了演示一些 intent 过滤器行为,下面的例子 从社交分享应用的清单文件中获取:

<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>

第一个 activity MainActivity 是应用的主入口点, 当用户首次使用启动器图标启动应用时打开:

  • ACTION_MAIN 操作 表示这是主要入口点,不需要任何 intent 数据。
  • CATEGORY_LAUNCHER 类别表明此活动的 图标应放置在系统的应用启动器中。如果 <activity> 元素 未使用 icon 指定图标,则系统将使用 <application> 中的图标 元素。

这两个元素必须配对使用,Activity 才会显示在应用启动器中。

第二个 activity ShareActivity 旨在方便分享文本和媒体内容 内容。尽管用户可能会通过从 MainActivity 进入此活动, 用户也可以从发出隐式提示的ShareActivity 匹配两个 intent 过滤器之一的 intent。

注意:MIME 类型 application/vnd.google.panorama360+jpg 是一种特殊的数据类型, 全景照片拍摄,您可以使用 Google panorama API。

将 intent 与其他应用的 intent 匹配intent 过滤器

如果其他应用以 Android 13(API 级别 33)或更高版本为目标平台,它可以处理您的 仅当您的 intent 与应用的操作和类别匹配时 <intent-filter> 元素。如果系统没有找到 匹配,则会抛出 ActivityNotFoundException。 发送邮件的应用必须处理 此例外情况。

同样,如果您更新应用,使其以 Android 13 为目标平台 或更高版本的 intent,源自外部应用的所有 intent 都会传递给 应用的导出组件 应用声明的 <intent-filter> 元素的类别。此行为 而不会考虑发送应用的目标 SDK 版本。

在以下情况下,不强制执行 intent 匹配:

  • 传送到未声明任何 intent 过滤器的组件中的 intent。
  • 源自同一应用内的 intent。
  • 源自系统的 intent;也就是说,从“系统 UID”(uid=1000) 发送的 intent。系统应用包括 system_server 和将 android:sharedUserId 设置为 android.uid.system 的应用。
  • 源自根的 intent。

详细了解意图匹配

使用待定 Intent

PendingIntent 对象是 Intent 对象的封装容器。PendingIntent 的主要用途 就是向外部应用授予权限 使用包含的 Intent,就像是从 应用自己的进程。

待定 Intent 的主要用例包括:

就像每个 Intent 对象都设计为由特定 应用组件的类型(ActivityServiceBroadcastReceiver),因此 PendingIntent 必须为 也是出于同样的考虑而创建的使用待定 intent 时,您的应用 通过调用 startActivity() 来执行 intent。相反,在创建 PendingIntent:通过调用相应的 Creator 方法:

除非您的应用正在接收来自其他应用的待处理 intent,否则, 上述用于创建 PendingIntent 的方法可能是 PendingIntent 方法。

每个方法都会接受当前应用 Context,即 Intent,以及一个或多个 应如何使用 intent(例如 intent 是否可多次使用)。

如需详细了解如何使用待处理 intent,请参阅每个 intent 的相关文档 通知应用微件 API 指南。

指定可变性

如果您的应用以 Android 12 或更高版本为目标平台,您必须指定 应用创建的每个 PendingIntent 对象的可变性。为了声明 给定 PendingIntent 对象可变或不可变,请使用 PendingIntent.FLAG_MUTABLEPendingIntent.FLAG_IMMUTABLE 标志。

如果您的应用尝试创建 PendingIntent 对象 那么系统会抛出一个 IllegalArgumentExceptionLogcat 中会显示以下消息:

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.

尽可能创建不可变的待处理 intent

在大多数情况下,您的应用应创建不可变的 PendingIntent 对象,如以下代码段所示。如果 PendingIntent 对象不可变, 则其他应用无法修改 intent 来调整调用 intent。

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
  • 使用实例将对话置于对话泡中 共 PendingIntent 个。可变的 PendingIntent 对象允许系统应用 正确的标记,例如 FLAG_ACTIVITY_MULTIPLE_TASKFLAG_ACTIVITY_NEW_DOCUMENT
  • 通过调用 来请求设备位置信息 requestLocationUpdates() 或类似 API。可变的 PendingIntent 对象允许系统添加 intent extra 表示位置信息生命周期事件。这些事件包括 位置变更,以及有可用的提供商。
  • 使用 AlarmManager 安排闹钟。 可变的 PendingIntent 对象允许系统添加 EXTRA_ALARM_COUNT intent extra。这个 extra 表示重复闹钟的次数 被触发通过包含此 extra,intent 可以准确地通知 判断重复闹钟是否被多次触发,例如 当设备处于休眠状态时触发。

如果您的应用会创建可变的 PendingIntent 对象,强烈建议您 使用显式 intent 并填写 ComponentName。如此一来,每当另一个应用调用 PendingIntent 并将控制权传回您的应用时,应用中的相同组件都会启动。

在待处理 intent 中使用显式 intent

为了更好地定义其他应用可以如何使用您应用的待处理 intent,请务必 将待处理 intent 封装在显式 intent 中。 为帮助遵循此最佳实践,请执行以下操作:

  1. 检查基本 intent 的操作、软件包和组件字段 。
  2. 使用 FLAG_IMMUTABLE。 Android 6.0(API 级别 23)中引入的新增功能,用于创建待处理 intent。此标志 阻止收到 PendingIntent 的应用填充 未填充的属性。如果您的应用的 minSdkVersion22 或更低版本,您可以同时提供安全性和兼容性 使用以下代码:

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

Intent 解析

当系统收到启动 Activity 的隐式 Intent 时,它会搜索 根据以下三个方面将 intent 与 intent 过滤器进行比较,找出符合 intent 的最佳 activity:

  • 行动。
  • 数据(URI 和数据类型)。
  • 类别。

以下部分介绍了如何将意图与相应组件匹配 。

操作测试

要指定接受的 intent 操作,intent 过滤器可以声明 0 个或多个 <action> 元素,如以下示例所示:

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

如需传递此过滤条件,Intent 中指定的操作 必须符合过滤条件中列出的某项操作。

如果过滤器未列出任何操作, intent 来匹配,因此所有 intent 都无法通过测试。但是,如果 Intent 未指定操作,则只要符合 至少包含一个操作。

类别测试

要指定接受的 intent 类别,intent 过滤器可以声明零个或多个 <category> 元素,如以下示例所示:

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

为了让 intent 通过类别测试,Intent 中的每个类别 必须与过滤器中的类别匹配。反之则不是必需的,intent 过滤器可以 声明的类别多于 IntentIntent仍然通过。因此,没有类别的 intent 无论过滤器中声明了什么类别,始终都会通过此测试。

注意: Android 会自动应用 CATEGORY_DEFAULT 类别 传递给 startActivity()startActivityForResult() 的所有隐式 intent。 如果您想让 Activity 接收隐式 Intent,则它必须 在其 intent 过滤器中为 "android.intent.category.DEFAULT" 添加一个类别,例如 如前面的 <intent-filter> 示例所示。

数据测试

要指定接受的 intent 数据,intent 过滤器可以声明零个或多个 <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 的每个部分都是 属性:schemehostport、 和 path

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

以下示例展示了这些属性的可能值:

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

在此 URI 中,架构为 content,主机为 com.example.project, 端口为 200,路径为 folder/subfolder/etc

上述每个属性在 <data> 元素中都是可选的, 但存在线性依赖关系:

  • 如果未指定架构,则会忽略主机。
  • 如果未指定主机,则会忽略端口。
  • 如果未指定架构和主机,则会忽略路径。

将 intent 中的 URI 与过滤器中的 URI 规范进行比较时, 并且仅与过滤器中包含的 URI 部分进行比较。例如:

  • 如果过滤器仅指定架构,则具有该架构的所有 URI 均匹配 。
  • 如果过滤器指定架构和授权,但未指定路径,则所有 URI 具有相同架构和授权的过滤器通过过滤器的过滤,而不考虑其路径。
  • 如果过滤器指定架构、授权方和路径,则仅指定具有相同架构的 URI, 授权方,路径传递过滤器。

注意:路径规范可以 包含星号通配符 (*),因此只需要路径名部分匹配。

数据测试会将 intent 中的 URI 和 MIME 类型与 URI 进行比较 和过滤器中指定的 MIME 类型。规则如下:

  1. 既不包含 URI 也不含 MIME 类型的 Intent,会将 仅当过滤器未指定任何 URI 或 MIME 类型时才能进行测试。
  2. 包含 URI 但不包含 MIME 类型(既未显式声明,也无法根据 URI)通过测试。 而过滤器同样不指定 MIME 类型。
  3. 包含 MIME 类型但不包含 URI 的 intent 通过了测试 仅当过滤器列出相同的 MIME 类型且未指定 URI 格式时。
  4. 一个同时包含 URI 和 MIME 类型(显式或可通过 URI)通过测试的 MIME 类型部分,前提是 type 与过滤条件中列出的类型匹配。它通过了测试的 URI 部分 如果其 URI 与过滤器中的 URI 匹配,或者其具有 content:file: URI,且过滤器未指定 URI。也就是说, 如果存在以下情况,则假定组件支持 content:file: 数据 其过滤条件只会列出 MIME 类型

注意:如果 intent 指定了 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>

Intent 匹配

根据 intent 过滤器匹配 intent,不仅有助于发现目标 而且还能发现一些有关 组件。例如,Home 应用会填充应用启动器 方法是使用指定 ACTION_MAIN 操作和 “CATEGORY_LAUNCHER”类别。 只有当 Intent 中的操作和类别匹配时,匹配才会成功 (如 IntentFilter 的文档中所述) 类。

您的应用可以使用与 Home 应用类似的方式使用 intent 匹配。 PackageManager 具有一组 query...() 这些方法可返回可接受特定 intent 的所有组件, 一系列类似的 resolve...() 方法,用于确定 组件响应 intent。例如: queryIntentActivities() 会返回可执行的所有 activity 的列表 作为参数传递的 intent,而 queryIntentServices() 会返回一个类似的服务列表。 这两种方法都不会激活组件;他们只会列出 响应速度。有一种类似的方法, queryBroadcastReceivers(),适用于广播接收器。