全面总结之 Intent篇

我们在前面的 Activity、Service 和 BroadcastReceiver 中重复的用到 Intent,我们用它来启动 Activity、启动 Service、传递广播。它作为一个消息传递的对象,活跃在各个组件之间。

Intent 的分类

Intent 可以分为两种类型:

  • 显示 Intent:直接(使用类名)指定要启动的组件。例如跳转到一个 Activity、启动一个新的服务,系统将立即启动 Intent 对象中指定的应用组件。构建显示 Intent 只需要指定组件名称,其余属性均为可选。

  • 隐式 Intent:不会直接指定特定的组件,而是声明要执行的动作。Android 系统通过将 Intent 的内容与在设备上的其他应用的清单文件中声明的 Intent 过滤器进行比较,从而找到要启动的组件,如果找到了,则启动该组件并向其传递 Intent 对象,如果有多个过滤器可以响应,则由用户来选择启动哪一个。

构建一个 Intent

Intent 对象携带了 Android 系统用来确定要启动哪个组件的信息(例如,准确的组件名称或应当接收该 Intent 的组件类别),以及收件人组件为了正确执行操作而使用的信息(例如,要采取的操作以及要处理的数据)。

一个 Intent 包含如下主要信息:

  • 组件名称(ComponentName):表示要启动的组件,该选项是可选的,但是显示 Intent 一定要有,如果没有组件名称,则该 Intent 就是隐式 Intent。
    我们可以使用 setComponent()setClass()setClassName() 或者 Intent 的构造函数来设置组件名称。

  • 操作(Action):使用一个字符串来指定要执行的通用操作,供 Intent 在您的应用中使用。
    我们可以使用 setAction() 或 Intent 的构造函数来指定操作。
    以下是一些用于启动 Activity 的常见操作:

    • ACTION_VIEW:如果你拥有一些某项 Activity 可向用户展示的信息(例如,要使用图库应用查看的照片;或者要使用地图应用查看的地址),请吃使用此操作与 startActivity() 结合使用。

    • ACTION_SEND:这也成为“共享” Intent。如果您拥有的一些用户可通过其他应用(例如,电子邮件应用或社交共享应用)共享的数据,则应使用 Intent 将此操作与 startActivity() 结合使用。

  • 数据(Data):引用待操作数据和/或该数据 MIME 类型的 URI。提供的数据类型通常由 Intent 的操作决定。
    创建 Intent 时,除了指定 URI 以外,指定数据类型往往也很重要,例如,能够显示图像的 Activity 可能无法播放音频文件,即便 URI 格式十分类似时也是如此。因此,指定数据的 MIME 类型有助于 Android 系统找到接收 Intent 的最佳组件。
    我们可以通过 setData() 来设置数据;通过调用 setType() 来设置 MIME 类型;也可以通过 setDataAndType() 同时显式设置两者。

  • 类别(Category):一个包含应处理 Intent 组件类型的附加信息的字符串。你可以将任意数量的类别描述放入到一个 Intent 中,但大多数 Intent 均不需要类别。
    我们可以使用 addCategory() 指定类别。
    以下是一些常见类别:

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

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

  • 额外数据(Extra):用来携带完成请求操作所需的附加信息的键值对。
    我们使用 putExtra() 方法添加 Extra 数据,还可以创建一个包含所有 extra 数据的 Bundle 对象,然后使用 putExtras() 将 Bundle 插入到 Intent 中。

  • 标志(Flag):在 Intent 类中定义的、充当 Intent 元数据的标志。标志可以指示 Android 系统如何启动 Activity,以及启动后如何处理。
    可以使用 setFlags() 方法设置标志。

接收 Intent

显示 Intent 就不用说了,指定哪个组件接收,就是哪个组件接收,实锤没啥好说的。

但是对于隐式 Intent 来说,要决定该组件可以接收哪些隐式 Intent,需要在清单文件中使用 <intent-filter> 元素为每个应用组件声明一个或者多个过滤器。然后该过滤器根据 Intent 的 操作(action)、数据(data)、类别(category)来确定自身是否可以响应该隐式 Intent,当隐式 Intent 可以通过某个过滤器时,系统才会将该 Intent 传递给该组件。当有多个组件可以响应的时候,则弹出提示框由用户选择使用哪个组件响应。

<intent-filter> 内部,可以使用三个元素中的一个或者多个来指定该组件可以响应哪些 Intent:

  • <action>:表示可以接收的 Intent 的操作(action),该值必须是文本字符串值。

  • <data>:表示可以接收的 Intent 数据类型(data),可以由一个或者多个指定数据 URI 和 MIME 类型表示。

  • <category>:声明可以接收的 Intent 类别(category),该值必须是文本字符串。

为了接收隐式 Intent,必须将 android.intent.category.DEFAULT 类别包括在 <intent-filter> 中,如果没有在过滤器中声明此类别,则隐式 Intent 不会解析为您的 Activity。

有以下几个点需要注意:

  1. 一个组件可以声明多个过滤器。

  2. 一个过滤器可以包含多个 actiondatacategory

  3. 创建过滤器时,仅需确定组件能够处理这些过滤器元素的任何及所有组合即可。

  4. 系统会将 Intent 与过滤器中的元素进行比较,只有完全通过,才会将 Intent 传递给该组件。

  5. 由于一个组件可以有多个过滤器,一个过滤器没有通过,Intent 可能会通过另一个过滤器。

  6. Activity 必须在清单中声明 Intent 过滤器,广播接收器可以在 Java 文件中调用 registerReceiver() 方法动态注册,调用 unregisterReceiver() 注销。

  7. 对于服务(Service)来说,请使用显示 Intent。

Intent 的解析

当系统接收到隐式 Intent 以启动 Activity 时,它根据以下三个方面将该 Intent 与 <intent-filter> 进行比较,以搜索该 Intent 的最佳响应 Activity:

  • Intent 操作(action)

  • Intent 数据(URI 和 MIME 类型)

  • Intent 类别

操作匹配

前面说过,一个 intent-filter 可以不声明 <acion> 元素,也可以有多个 <acion> 元素。

如果要使 Intent 通过该过滤器,则 Intent 中指定的 action(通过 setAction() 设置) 必须与过滤器中列出的某一个 action 元素匹配。如若不匹配,则不会通过该过滤器。

数据匹配

intent-filter 可以不声明 data 元素,也可以声明多个 data 元素。

每个 data 元素都可以指定 URI 结构和 MIME 类型。

URI 的每个部分均包含单独的 schemehostportpath 属性。

例如在 content://com.example.project:200/folder/subfolder/etc 中:

  • scheme 是 content

  • host 是 com.example.project

  • port 是 200

  • path 是 folder/subfolder/etc

在 intent-filter 的 data 元素中,上述的每个属性均为可选,但存在线性依赖关系:

  • 如果未指定 scheme,则会忽略 host

  • 如果未指定 host,则会忽略 port

  • 如果未指定 scheme 和 host,则会忽略 path

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

  • 如果 intent-filter 仅指定 scheme,则具有该 scheme 的所有 URI 均与该过滤器匹配

  • 如果 intent-filter 指定 scheme 和 host,但没有指定 path,则具有相同 scheme 和 host 的所有 URI 都会通过过滤器。

  • 如果 intent-filter 指定 scheme、host 和 path,那么仅具有相同 scheme、host 和 path 的 URI 才会通过过滤器。

path 可以使用通配符 * 来替代,因此仅需部分匹配 path 即可。

对于 Intent 中的 URI 和 MIME 类型与 intent-filter 中的 URI 和 MIME 类型比较规则如下:

  • 仅当 intent-filter 没有指定任何 URI 或 MIME 类型时,不包含 URI 和 MIME 类型的 Intent 才能通过。

  • intent-filter 没有指定 MIME 类型只指定了 URI 的时候,只有同样包含了可以匹配的 URI 并且同样没有指定 MIME(没有显式指定,也无法通过 URI 推断) 的 Intent 可以通过。

  • 仅当 intent-filter 列出相同的 MIME 类型 且没有指定 URI 时候,匹配 MIME 类型但不包含 URI 的 Intent 才会通过过滤器。

  • 仅当 MIME 类型与过滤器中列出的类型匹配时,同时包含 URI 类型和 MIME 类型(通过显式声明,或可以通过 URI 推断得出)的 Intent 才会通过测试的 MIME 类型部分。 如果 Intent 的 URI 与过滤器中的 URI 匹配,或者如果 Intent 具有 content: 或 file: URI 且过滤器未指定 URI,则 Intent 会通过测试的 URI 部分。 换言之,如果过滤器只是列出 MIME 类型,则假定组件支持 content: 和 file: 数据。

类别匹配

intent-filter 可以不声明任何 category 元素,也可以声明多个。

Intent 要想通过 intent-filter,那么 Intent 中的每个 catetory(通过 addCategory() 添加)都必须和 intent-filter 中的 category 元素匹配。

相反,intent-filter 中可以包含多个 category,但只有部分 category 元素可以响应某个 Intent,那么该 Intent 同样可以通过该 intent-filter。

如需 Activity 接收隐式 Intent,则必须将 "android.intent.category.DEFAULT" 的类别包括在其 Intent 过滤器中。

特殊的 Intent —— PendingIntent

PendingIntent 是 Intent 的包装器。PengingIntent 的主要目的是授权外部 App 使用本 App 中包含的 Intent,就像是它从你的 App 本身进行中执行的一样。

PendingIntent 的主要用例包括:

  • 声明用户使用您的通知执行操作时所要执行的 Intent(Android 系统的 NotificationManager 执行 Intent)。

  • 声明用户使用您的 应用小部件执行操作时要执行的 Intent(主屏幕应用执行 Intent)。

  • 声明未来某一特定时间要执行的 Intent(Android 系统的 AlarmManager 执行 Intent)。

由于每个 Intent 对象均设计为由特定类型的应用组件(Activity、Service 或 BroadcastReceiver)进行处理,因此还必须基于相同的考虑因素创建 PendingIntent。使用待定 Intent 时,应用不会使用调用(如 startActivity())执行该 Intent。相反,通过调用相应的创建器方法创建 PendingIntent 时,您必须声明所需的组件类型:

  • PendingIntent.getActivity(),适用于启动 Activity 的 Intent。

  • PendingIntent.getService(),适用于启动 Service 的 Intent。

  • PendingIntent.getBroadcast(),适用于启动

BroadcastReceiver 的 Intent。
除非您的应用正在从其他应用中接收待定 Intent,否则上述用于创建 PendingIntent 的方法可能是您所需的唯一 PendingIntent 方法。

每种方法均会提取当前的应用 Context、您要包装的 Intent 以及一个或多个指定应如何使用该 Intent 的标志(例如,是否可以多次使用该 Intent)。

一个 Notification 的例子:

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
mBuilder.setSmallIcon(R.drawable.notification_icon)
mBuilder.setContentTitle("My notification")
mBuilder.setContentText("Hello World!");
Intent resultIntent = new Intent(this, ResultActivity.class);
PendingIntent resultPendingIntent = PendingIntent.getActivity(this,0,resultIntent,PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
int mNotificationId = 001;
// Gets an instance of the NotificationManager service
NotificationManager mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Builds the notification and issues it.
mNotifyMgr.notify(mNotificationId, mBuilder.build());

通用 Intent

请参考: 通用 Intent

参考

Copyright© 2020-2022 li-xyz 冀ICP备2022001112号-1