Android开发 - Intent

优质
小牛编辑
135浏览
2023-12-01

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

  • 启动Activity:startActivity()

  • 启动服务:bindService()

  • 传递广播:sendBroadcast()

Intent 类型

Intent 分为两种类型:

  • 显式 Intent按名称(完全限定类名)指定要启动的组件

  • 隐式 Intent不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理它

创建显式 Intent 启动 Activity 或服务时,系统将立即启动 Intent 对象中指定的应用组件。

Intent - 图1

隐式 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 元素,也可以声明多个此类元素。例如:

  1. <intent-filter>
  2. <action android:name="android.intent.action.EDIT" />
  3. <action android:name="android.intent.action.VIEW" />
  4. ...
  5. </intent-filter>

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

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

类别(Category)匹配

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

  1. <intent-filter>
  2. <category android:name="android.intent.category.DEFAULT" />
  3. <category android:name="android.intent.category.BROWSABLE" />
  4. ...
  5. </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 元素,也可以声明多个此类元素。例如:

  1. <intent-filter>
  2. <data android:mimeType="video/mpeg" android:scheme="http" ... />
  3. <data android:mimeType="audio/mpeg" android:scheme="http" ... />
  4. ...
  5. </intent-filter>

每个 <data> 元素均可指定 URI 结构和数据类型(MIME 介质类型)。URI 的每个部分均包含单独的 scheme、host、port 和 path 属性:

  1. scheme://host:port/path

例如:

  1. 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 指出,组件可从 内容提供者 处获得并显示图像数据。

  1. <intent-filter>
  2. <data android:mimeType="image/*" />
  3. ...
  4. </intent-filter>

Intent 匹配

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