Intent
Intent 是一个消息传递对象,您可以使用它从其他应用组件请求操作。尽管 Intent 可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个:
启动Activity:
startActivity()
启动服务:
bindService()
传递广播:
sendBroadcast()
Intent 类型
Intent 分为两种类型:
显式 Intent:按名称(完全限定类名)指定要启动的组件。
隐式 Intent:不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理它。
创建显式 Intent
启动 Activity 或服务时,系统将立即启动 Intent 对象中指定的应用组件。
隐式 Intent 如何通过系统传递以启动其他 Activity 的图解
创建隐式 Intent
时,Android 系统通过将 Intent 的内容与在设备上其他应用的清单文件中声明的 Intent 过滤器进行比较,从而找到要启动的相应组件。Intent如果 Intent 与 Intent 过滤器匹配,则系统将启动该组件,并将其传递给对象。如果多个 Intent 过滤器兼容,则系统会显示一个对话框,支持用户选取要使用的应用。
为了确保应用的安全性,启动 Service 时,请始终使用显式 Intent,且不要为服务声明 Intent 过滤器。从 Android 5.0(API 级别 21)开始,如果使用隐式 Intent 调用 bindService(),系统会抛出异常。
构建 Intent
组件名称(ComponentName):这是可选项,但也是构建显式 Intent 的一项重要信息,这意味着 Intent 应当仅传递给由组件名称定义的应用组件。Intent 的这一字段是
ComponentName
对象,您可以使用目标组件的完全限定类名指定此对象,其中包括应用的软件包名称。操作(Action):指定要执行的通用操作(例如,“查看”或“选取”)的字符串。
数据(Data):引用待操作数据和/或该数据 MIME 类型的 URI(Uri 对象)。提供的数据类型通常由 Intent 的操作决定。
要仅设置数据 URI,请调用 setData()。要仅设置 MIME 类型,请调用 setType()。如有必要,您可以使用 setDataAndType() 同时显式设置二者。
警告:若要同时设置 URI 和 MIME 类型,请勿调用 setData() 和 setType(),因为它们会互相抵消彼此的值。请始终使用 setDataAndType() 同时设置 URI 和 MIME 类型。
类别(Category):一个包含应处理 Intent 组件类型的附加信息的字符串。您可以将任意数量的类别描述放入一个 Intent 中,但大多数 Intent 均不需要类别。
Extra:携带完成请求操作所需的附加信息的键值对。正如某些操作使用特定类型的数据 URI 一样,有些操作也使用特定的附加数据。例如,使用
ACTION_SEND
创建用于发送电子邮件的 Intent 时,可以使用EXTRA_EMAIL
键指定“目标”收件人,并使用EXTRA_SUBJECT
键指定“主题”。标志(Flags):在 Intent 类中定义的、充当 Intent 元数据的标志。标志可以指示 Android 系统如何启动 Activity(例如,Activity 应属于哪个Task )。
Intent 解析
当系统收到隐式 Intent 以启动 Activity 时,它根据以下三个方面将该 Intent 与 Intent 过滤器进行比较,搜索该 Intent 的最佳 Activity:
Intent 操作
Intent 数据(URI 和数据类型)
Intent 类别
系统通过将 Intent 与所有这三个元素进行比较,根据过滤器测试隐式 Intent。隐式 Intent 若要传递给组件,必须通过所有这三项测试。如果 Intent 甚至无法匹配其中任何一项测试,则 Android 系统不会将其传递给组件。但是,由于一个组件可能有多个 Intent 过滤器,因此未能通过某一组件过滤器的 Intent 可能会通过另一过滤器。(在Demo中实验了几次,发现 Action 和 Data 必须至少设置一个,否则不能匹配到)
操作(Action)匹配
要指定接受的 Intent 操作, Intent 过滤器既可以不声明任何 action
元素,也可以声明多个此类元素。例如:
<intent-filter>
<action android:name="android.intent.action.EDIT" />
<action android:name="android.intent.action.VIEW" />
...
</intent-filter>
要通过此过滤器,您在 Intent 中指定的操作必须与过滤器中列出的 某一操作匹配。
如果该过滤器未列出任何操作,则 Intent 没有任何匹配项,因此所有 Intent 均无法通过测试。但是,如果 Intent 未指定操作,则会通过测试(只要过滤器至少包含一个操作)。
类别(Category)匹配
要指定接受的 Intent 类别, Intent 过滤器既可以不声明任何 category
元素,也可以声明多个此类元素。例如:
<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 过滤器中。
数据(Data)匹配
要指定接受的 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 的每个部分均包含单独的 scheme、host、port 和 path 属性:
scheme://host:port/path
例如:
content://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 类型进行比较。规则如下:
仅当过滤器未指定任何 URI 或 MIME 类型时,不含 URI 和 MIME 类型的 Intent 才会通过测试。
对于包含 URI、但不含 MIME 类型(既未显式声明,也无法通过 URI 推断得出)的 Intent,仅当其 URI 与过滤器的 URI 格式匹配、且过滤器同样未指定 MIME 类型时,才会通过测试。
仅当过滤器列出相同的 MIME 类型且未指定 URI 格式时,包含 MIME 类型、但不含 URI 的 Intent 才会通过测试。
仅当 MIME 类型与过滤器中列出的类型匹配时,包含 URI 和 MIME 类型(通过显式声明,或可以通过 URI 推断得出)的 Intent 才会通过测试的 MIME 类型部分。如果 Intent 的 URI 与过滤器中的 URI 匹配,或者如果 Intent 具有
content:
或file:
URI 且过滤器未指定 URI,则 Intent 会通过测试的 URI 部分。换而言之,如果过滤器仅列出 MIME 类型,则假定组件支持content:
和file:
数据。
最后一条规则,反映了期望组件能够从文件中或 内容提供者
处获得本地数据。因此,其过滤器可以仅列出数据类型,而不必显式命名 content:
和 file:
架构。这是一个典型的案例。例如,下文中的 data
元素向 Android 指出,组件可从 内容提供者
处获得并显示图像数据。
<intent-filter>
<data android:mimeType="image/*" />
...
</intent-filter>
Intent 匹配
您的应用可以采用类似的方式使用 Intent 匹配。PackageManager
提供了一整套 query...()
方法来返回所有能够接受特定 Intent 的组件。此外,它还提供了一系列类似的 resolve...()
方法来确定响应 Intent 的最佳组件。例如,queryIntentActivities()
将返回能够执行那些作为参数传递的 Intent 的所有 Activity 列表,而 queryIntentServices()
则可返回类似的服务列表。这两种方法均不会激活组件,而只是列出能够响应的组件。对于广播接收器,有一种类似的方法: queryBroadcastReceivers()
。