本文主要参考Google develop官方文档结合开发实践所写。
参考连接:
https://developer.android.google.cn/guide/components/intents-filters?hl=zh-cn
https://developer.android.google.cn/guide/components/services?hl=zh-cn
Intent 和 Intent 过滤器
Intent
是一个消息传递对象,您可以用来从其他应用组件请求操作。尽管 Intent 可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个:
- 启动 Activity
Activity
表示应用中的一个屏幕。通过将Intent
传递给startActivity()
,您可以启动新的Activity
实例。Intent
用于描述要启动的 Activity,并携带任何必要的数据。/** * Same as {@link #startActivity(Intent, Bundle)} with no options * specified. * * @param intent The intent to start. * * @throws android.content.ActivityNotFoundException * * @see #startActivity(Intent, Bundle) * @see #startActivityForResult */ @Override public void startActivity(Intent intent) { this.startActivity(intent, null); }
/** * Launch a new activity. You will not receive any information about when * the activity exits. This implementation overrides the base version, * providing information about * the activity performing the launch. Because of this additional * information, the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag is not * required; if not specified, the new activity will be added to the * task of the caller. * * <p>This method throws {@link android.content.ActivityNotFoundException} * if there was no Activity found to run the given Intent. * * @param intent The intent to start. * @param options Additional options for how the Activity should be started. * See {@link android.content.Context#startActivity(Intent, Bundle)} * Context.startActivity(Intent, Bundle)} for more details. * * @throws android.content.ActivityNotFoundException * * @see #startActivity(Intent) * @see #startActivityForResult */ @Override public void startActivity(Intent intent, @Nullable Bundle options) { if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN) && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY)) { if (TextUtils.equals(getPackageName(), intent.resolveActivity(getPackageManager()).getPackageName())) { // Apply Autofill restore mechanism on the started activity by startActivity() final IBinder token = mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN); // Remove restore ability from current activity mIntent.removeExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN); mIntent.removeExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY); // Put restore token intent.putExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN, token); intent.putExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY, true); } } if (options != null) { startActivityForResult(intent, -1, options); } else { // Note we want to go through this call for compatibility with // applications that may have overridden the method. startActivityForResult(intent, -1); } }
如果您希望在 Activity 完成后收到结果,请调用 startActivityForResult()
。在 Activity 的 onActivityResult()
回调中,您的 Activity 将结果作为单独的 Intent
对象接收。
- 启动Activity时携带的数据都有什么?
- 什么情况下在启动Activity时需要返回结果?有什么实际意义?
- 启动服务
Service
是一个不使用用户界面而在后台执行操作的组件。使用 Android 5.0(API 级别 21)及更高版本,您可以启动包含JobScheduler
的服务。对于 Android 5.0(API 级别 21)之前的版本,您可以使用
Service
类的方法来启动服务。通过将Intent
传递给startService()
,您可以启动服务执行一次性操作(例如,下载文件)。Intent
用于描述要启动的服务,并携带任何必要的数据。如果服务旨在使用客户端-服务器接口,则通过将
Intent
传递给bindService()
,您可以从其他组件绑定到此服务。 - 传递广播
广播是任何应用均可接收的消息。系统将针对系统事件(例如:系统启动或设备开始充电时)传递各种广播。通过将
Intent
传递给sendBroadcast()
或sendOrderedBroadcast()
,您可以将广播传递给其他应用。
Intent 类型
Intent 分为两种类型:
- 显式 Intent:通过提供目标应用的软件包名称或完全限定的组件类名来指定可处理 Intent 的应用。通常,您会在自己的应用中使用显式 Intent 来启动组件,这是因为您知道要启动的 Activity 或服务的类名。例如,您可能会启动您应用内的新 Activity 以响应用户操作,或者启动服务以在后台下载文件。
- 隐式 Intent :不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理。例如,如需在地图上向用户显示位置,则可以使用隐式 Intent,请求另一具有此功能的应用在地图上显示指定的位置。
图 1 显示如何在启动 Activity 时使用 Intent。当 Intent
对象显式命名某个具体的 Activity 组件时,系统立即启动该组件。
图 1. 隐式 Intent 如何通过系统传递以启动其他 Activity:
[1] Activity A 创建包含操作描述的 Intent
,并将其传递给 startActivity()
。
[2] Android 系统搜索所有应用中与 Intent 匹配的 Intent 过滤器。找到匹配项之后,
[3] 该系统通过调用匹配 Activity (Activity B) 的 onCreate()
方法并将其传递给 Intent
,以此启动匹配 Activity。
使用隐式 Intent 时,Android 系统通过将 Intent 的内容与在设备上其他应用的清单文件中声明的 Intent - Filter进行比较,从而找到要启动的相应组件。如果 Intent 与 Intent - Filter匹配,则系统将启动该组件,并向其传递 Intent
对象。如果多个 Intent 过滤器兼容,则系统会显示一个对话框,支持用户选取要使用的应用。
Intent - Filter是应用清单文件中的一个表达式,用于指定该组件要接收的 Intent 类型。例如,通过为 Activity 声明 Intent - Filter,您可以使其他应用能够直接使用某一特定类型的 Intent 启动 Activity。同样,如果您没有为 Activity 声明任何 Intent 过滤器,则 Activity 只能通过显式 Intent 启动。
这句话有两层明显的含义,
- 如果希望某个Activity能够被外部其他应用或者服务使用,则必须定义其他Activity可识别的Intent - Filter;
- 如果不希望该Activity被外部使用,则该Activity就不应该定义Intent - Filter;
注意:为了确保应用的安全性,启动 Service
时,请始终使用显式 Intent,且不要为服务声明 Intent 过滤器。使用隐式 Intent 启动服务存在安全隐患,因为您无法确定哪些服务将响应 Intent,且用户无法看到哪些服务已启动。从 Android 5.0(API 级别 21)开始,如果使用隐式 Intent 调用 bindService()
,系统会抛出异常。
构建 Intent
Intent
对象携带 Android 系统用来确定要启动哪个组件的信息(例如,准确的组件名称或应当接收该 Intent 的组件类别),以及收件人组件为了正确执行操作而使用的信息(例如,要采取的操作以及要处理的数据)。
Intent
中包含的主要信息如下:
组件名称
要启动的组件名称。
这是可选项,但也是构建显式 Intent 的一项重要信息,这意味着 Intent 应当仅传递给由组件名称定义的应用组件。如果没有组件名称,则 Intent 则为隐式,且系统将根据其他 Intent 信息(例如,以下所述的操作、数据和类别)决定哪个组件应当接收 Intent。如需在应用中启动特定的组件,则应指定该组件的名称。
请注意:启动 Service
时,应始终指定组件名称。否则,您无法确定哪项服务会响应 Intent,且用户无法看到哪项服务已启动。
Intent
的这一字段是 ComponentName
对象,您可以使用目标组件的完全限定类名指定此对象,其中包括应用的软件包名称。例如,com.example.ExampleActivity
。您可以使用 setComponent()
、setClass()
、setClassName()
,或 Intent
构造函数设置组件名称。
操作(action)
指定要执行的通用操作(例如,查看或选取)的字符串。
对于广播 Intent,这是指已发生且正在报告的操作。操作会在很大程度上决定其余 Intent 的构成,特别是数据和 extra 中包含的内容。
您可以指定自己的操作,供 Intent 在您的应用内使用(或者供其他应用在您的应用中调用组件)。但是,您通常应该使用由Intent
类或其他框架类定义的操作常量。以下是一些用于启动 Activity 的常见操作:
如果您拥有一些某项 Activity 可向用户显示的信息(例如,要使用图库应用查看的照片;或者要使用地图应用查看的地址),请通过 Intent 将此操作与 startActivity()
结合使用。
这也称为共享 Intent。如果您拥有一些用户可通过其他应用(例如,电子邮件应用或社交共享应用)共享的数据,则应使用 Intent 将此操作与 startActivity()
结合使用。
有关更多定义通用操作的常量,请参阅 Intent
类参考文档。其他操作在 Android 框架中的其他位置定义。例如,对于在系统的设置应用中打开特定屏幕的操作,将在 Settings
中定义。
您可以使用 setAction()
或 Intent
构造函数为 Intent 指定操作。
如以下示例所示,如果定义自己的操作,请确保加入应用的软件包名称作为前缀:
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
数据(Data)
引用待操作数据和/或该数据 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 类型。
类别(Category)
一个包含应处理 Intent 组件类型的附加信息的字符串。您可以将任意数量的类别描述放入一个 Intent 中,但大多数 Intent 均不需要类别。以下是一些常见类别:
目标 Activity 允许本身通过网络浏览器启动,以显示链接引用的数据,如图像或电子邮件。
该 Activity 是任务的初始 Activity,在系统的应用启动器中列出。
有关类别的完整列表,请参阅 Intent
类描述。
您可以使用 addCategory()
指定类别。
以上列出的这些属性(组件名称、操作、数据和类别)表示 Intent 的既定特征。通过读取这些属性,Android 系统能够解析应当启动哪个应用组件。但是,Intent 也有可能会携带一些不影响其如何解析为应用组件的信息。Intent 还可以提供以下信息:
Extra
携带完成请求操作所需的附加信息的键值对。正如某些操作使用特定类型的数据 URI 一样,有些操作也使用特定的 extra。
您可以使用各种 putExtra()
方法添加 extra 数据,每种方法均接受两个参数:键名和值。您还可以创建一个包含所有 extra 数据的 Bundle
对象,然后使用 putExtras()
将 Bundle
插入 Intent
中。从这句话中我们就可以看出,所谓的Extra其存储方式就是Bundle,将一个Bundle对象插入Intent也就完成了Extra的设置,使用putExtras()
方法完成。
例如,使用 ACTION_SEND
创建用于发送电子邮件的 Intent 时,可以使用 EXTRA_EMAIL
键指定目标收件人,并使用 EXTRA_SUBJECT
键指定主题。从上面这句话可以看出其键名称一般以“EXTRA_
”开始。
Intent
类将为标准化的数据类型指定多个 EXTRA_*
常量。如需声明自己的 extra 键(对于应用接收的 Intent),请确保将应用的软件包名称作为前缀,如下例所示:
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
注意:在发送您希望另一个应用接收的 Intent 时,请勿使用 Parcelable
或 Serializable
数据。如果某个应用尝试访问 Bundle
对象中的数据,但没有对打包或序列化类的访问权限,则系统将提出一个 RuntimeException
。
标志
标志在 Intent
类中定义,充当 Intent 的元数据。标志可以指示 Android 系统如何启动 Activity(例如,Activity 应属于哪个任务),以及启动之后如何处理(例如,Activity 是否属于最近的 Activity 列表)。
如需了解详细信息,请参阅 setFlags()
方法。如下,
/**
* Set special flags controlling how this intent is handled. Most values
* here depend on the type of component being executed by the Intent,
* specifically the FLAG_ACTIVITY_* flags are all for use with
* {@link Context#startActivity Context.startActivity()} and the
* FLAG_RECEIVER_* flags are all for use with
* {@link Context#sendBroadcast(Intent) Context.sendBroadcast()}.
*
* <p>See the
* <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
* Stack</a> documentation for important information on how some of these options impact
* the behavior of your application.
*
* @param flags The desired flags.
* @return Returns the same Intent object, for chaining multiple calls
* into a single statement.
* @see #getFlags
* @see #addFlags
* @see #removeFlags
*/
public @NonNull Intent setFlags(@Flags int flags) {
mFlags = flags;
return this;
}