Intent

1.什么是Intent?

Intent 是一个消息传递对象,可以用来从其他应用组件请求action。尽管 Intent 可以通过多种方式促进组件之间的通信,但其基本用法主要包括以下三个:

2.基本用法

  • 启动 Activity

    Intent` 用于描述要启动的 Activity,并携带任何必要的数据。

  • 启动服务

    通过将 Intent 传递给 startService(),您可以启动服务执行一次性操作(例如,下载文件)。Intent 用于描述要启动的服务,并携带任何必要的数据。

  • 传递广播

    广播是任何应用均可接收的消息

3.分为两种类型

  • 显式 Intent:(提供目标应用的软件包名称或完全限定的组件类名)通常,会在自己的应用中使用显式 Intent 来启动组件,这是因为您知道要启动的 Activity 或服务的类名。
  • 隐式 Intent :不会指定特定的组件,而是声明要执行的常规操作。比如:在浏览器中打开…

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

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

4.构建Intent

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

4.1组件名称ComponentName(可选)

​ 如果没有组件名称,则 Intent 则为隐式,且系统将根据其他 Intent 信息(例如,以下所述的操作、数据和类别)决定哪个组件应当接收 Intent。例如,com.example.ExampleActivity。您可以使用 setComponent()setClass()setClassName(),或 Intent 构造函数设置组件名称。

4.2操作/动作ACTION

​ 指定要执行的通用操作(例如,查看选取)的字符串。

​ 对于广播 Intent,这是指已发生且正在报告的操作。操作会在很大程度上决定其余 Intent 的构成,特别是数据和 extra 中包含的内容。以下是一些用于启动 Activity 的常见操作:

ACTION_VIEW

如果您拥有一些某项 Activity 可向用户显示信息(例如,要使用图库应用查看的照片;或者要使用地图应用查看的地址),请通过 Intent 将此操作与 startActivity() 结合使用。

ACTION_SEND

这也称为共享 Intent。如果您拥有一些用户可通过其他应用(例如,电子邮件应用或社交共享应用)共享的数据,则应使用 Intent 将此操作与 startActivity() 结合使用。有关更多定义通用操作的常量,请参阅 Intent 类参考文档。

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

如果定义自己的操作,请确保加入应用的软件包名称作为前缀:

static final String ACTION_TIMETRAVEL = static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";

4.3数据

引用待 操作 数据和/或该数据 MIME 类型的 URI(Uri 对象)。提供的数据类型通常由 Intent 的操作决定。例如,如果操作是 ACTION_EDIT,则数据应包含待编辑文档的 URI。

引用待操作数据和/或该数据 MIME 类型的 URI(Uri 对象)。提供的数据类型通常由 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 类型。

4.4类别

一个包含应处理 Intent 组件类型的附加信息的字符串。您可以将任意数量的类别描述放入一个 Intent 中,但大多数 Intent 均不需要类别。以下是一些常见类别:

  • CATEGORY_BROWSABLE

    目标 Activity 允许本身通过网络浏览器启动,以显示链接引用的数据,如图像或电子邮件。

  • CATEGORY_LAUNCHER

    该 Activity 是任务的初始 Activity,在系统的应用启动器中列出。

有关类别的完整列表,请参阅 Intent 类描述。

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

4.5Extra

携带完成请求操作所需的附加信息的键值对。正如某些操作使用特定类型的数据 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 = static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";

注意:在发送您希望另一个应用接收的 Intent 时,请勿使用 ParcelableSerializable 数据。如果某个应用尝试访问 Bundle 对象中的数据,但没有对打包或序列化类的访问权限,则系统将提出一个 RuntimeException

4.6标志FLAG

​ 标志在 Intent 类中定义,充当 Intent 的元数据。标志可以指示 Android 系统如何启动 Activity(例如,Activity 应属于哪个任务),以及启动之后如何处理(例如,Activity 是否属于最近的 Activity 列表)。

如需了解详细信息,请参阅 setFlags() 方法。

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

调用 startActivity() 时,系统将检查已安装的所有应用,确定哪些应用能够处理这种 Intent(即:含 ACTION_SEND 操作并携带“text/plain”数据的 Intent)。如果只有一个应用能够处理,则该应用将立即打开并为其提供 Intent。如果多个 Activity 接受 Intent,则系统将显示一个对话框(如图 2 所示),使用户能够选取要使用的应用。

intent到底发给哪个activity,需要进行三个匹配,一个是action,一个是category,一个是data。

理论上来说,如果intent不指定category,那么无论intent filter的内容是什么都应该是匹配的。但是,如果是implicit intent,android默认给加上一个CATEGORY_DEFAULT,这样的话如果intent filter中没有android.intent.category.DEFAULT这个category的话,匹配测试就会失败。所以,如果你的 activity支持接收implicit intent的话就一定要在intent filter中加入android.intent.category.DEFAULT。

https://developer.android.google.cn/guide/components/intents-filters#ActionTest

5.Intent 解析

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

  • 操作。
  • 数据(URI 和数据类型)。
  • 类别。

下文根据应用的清单文件中的 Intent 过滤器声明,描述 Intent 如何与相应的组件匹配。

操作ACTION测试

要指定接受的 Intent 操作,Intent 过滤器既可以不声明任何 `` 元素,也可以声明多个此类元素,如下例所示:

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

要通过此过滤器,您在 Intent 中指定的操作必须与过滤器中列出的某一操作匹配。

如果该过滤器未列出任何操作,则 Intent 没有任何匹配项,因此所有 Intent 均无法通过测试。但是,如果 Intent 未指定操作,则只要过滤器内包含至少一项操作,就可以通过测试。

类别测试

要指定接受的 Intent 类别,Intent 过滤器既可以不声明任何 `` 元素,也可以声明多个此类元素,如下例所示:

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

若要使 Intent 通过类别测试,则 Intent 中的每个类别均必须与过滤器中的类别匹配。反之则未必然,Intent 过滤器声明的类别可以超出 Intent 中指定的数量,且 Intent 仍会通过测试。因此,不含类别的 Intent 应当始终会通过此测试,无论过滤器中声明何种类别均是如此。

请注意:Android 会自动将 CATEGORY_DEFAULT 类别应用于传递给 startActivity()startActivityForResult() 的所有隐式 Intent。如需 Activity 接收隐式 Intent,则必须将 "android.intent.category.DEFAULT" 的类别包括在其 Intent 过滤器中(如上文的 `` 示例所示)。

数据测试

要指定接受的 Intent 数据,Intent 过滤器既可以不声明任何 `` 元素,也可以声明多个此类元素,如下例所示:

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

每个 `` 元素均可指定 URI 结构和数据类型(MIME 媒体类型)。URI 的每个部分都是一个单独的属性:schemehostportpath

://://:/

下例所示为这些属性的可能值:

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

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

`` 元素中,上述每个属性均为可选,但存在线性依赖关系:

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

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

  • 如果过滤器仅指定架构,则具有该架构的所有 URI 均与该过滤器匹配。
  • 如果过滤器指定架构和权限,但未指定路径,则具有相同架构和权限的所有 URI 都会通过过滤器,无论其路径如何均是如此。
  • 如果过滤器指定架构、权限和路径,则仅具有相同架构、权限和路径的 URI 才会通过过滤器。

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

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

  1. 仅当过滤器未指定任何 URI 或 MIME 类型时,不含 URI 和 MIME 类型的 Intent 才会通过测试。
  2. 对于包含 URI 但不含 MIME 类型(既未显式声明,也无法通过 URI 推断得出)的 Intent,仅当其 URI 与过滤器的 URI 格式匹配、且过滤器同样未指定 MIME 类型时,才会通过测试。
  3. 仅当过滤器列出相同的 MIME 类型且未指定 URI 格式时,包含 MIME 类型但不含 URI 的 Intent 才会通过测试。
  4. 仅当 MIME 类型与过滤器中列出的类型匹配时,同时包含 URI 类型和 MIME 类型(通过显式声明,或可以通过 URI 推断得出)的 Intent 才会通过测试的 MIME 类型部分。如果 Intent 的 URI 与过滤器中的 URI 匹配,或者如果 Intent 具有 content:file: URI 且过滤器未指定 URI,则 Intent 会通过测试的 URI 部分。换言之,如果过滤器只是列出 MIME 类型,则假定组件支持 content:file: 数据。

请注意:如果 Intent 指定 URI 或 MIME 类型,则数据测试会在 中没有 元素时失败。

最后一条规则,即规则 (d),反映出对组件能够从文件中或内容提供程序处获得本地数据的预期。因此,其过滤器只能列出数据类型,不需要显式命名 content:file: 架构。以下是一个典型示例,说明 `` 元素向 Android 指出,组件可从内容提供程序处获得并显示图像数据:

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

由于大部分可用数据均由内容提供程序分发,因此指定数据类型(而非 URI)的过滤器也许最为常见。

另一常见的配置是具有架构和数据类型的过滤器。例如,下文中的 `` 元素向 Android 指出,组件可从网络中检索视频数据以执行操作:

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

Intent 匹配

通过 Intent 过滤器匹配 Intent,这不仅有助于发现要激活的目标组件,还有助于发现设备上组件集的相关信息。例如,主页应用通过使用指定 ACTION_MAIN 操作和 CATEGORY_LAUNCHER 类别的 Intent 过滤器查找所有 Activity,以此填充应用启动器。如 IntentFilter 类文档所述,只有当 Intent 中的操作和类别与过滤器匹配时,匹配才会成功。

您的应用可以使用类似于主页应用的方式使用 Intent 匹配。PackageManager 提供一整套 query...() 方法来返回所有能够接受特定 Intent 的组件。此外,还会提供一系列类似的 resolve...() 方法来确定响应 Intent 的最佳组件。例如,queryIntentActivities() 将返回能够执行作为参数传递的 Intent 中列出的所有 Activity,而 queryIntentServices() 则可返回类似的一系列服务。这两种方法均不会激活组件;而只是列出能够响应的组件。对于广播接收器,有一种类似的方法:queryBroadcastReceivers()

实践心得

  1. intent可以打开别的应用的主Activity

    启动别的应用的其他的Activity会报异常SecurityException。主Activity是指设置了

的Activity,

  1. 浏览器列表要这样调用,否则只是用系统浏览器打开

    startActivity(Intent.createChooser(intent,”hi”));


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!