Android Intent
一、概述
Intent是Android四大组件通信桥梁,它负责在组件之间传递消息和数据,是一个消息传递对象。通过使用Intent,可以启动另一个活动Activity、服务Service、广播接收器BroadcastReceiver,以及发送和接收数据。
二、Intent的两种类型
显式Intent多用来实现一个应用内部的跳转,而隐式intent多用来实现应用与应用之间的跳转
显示Intent
明确指定了要启动的目标组件的名称。
值得注意的是从Android 5.0开始, Service 只能通过显式 Intent 启动,如果使用隐式 Intent 调用 bindService(),系统会引发异常。因为使用隐式 Intent 启动服务存在安全隐患,无法确定哪些服务将响应 Intent,且用户无法看到哪些服务已启动。
隐式Intent
不指定组件名,而是指定了可处理该Intent的某些组件的属性,如Action、Data、Category等,然后系统会根据这些属性去匹配AndroidManifest.xml相关组件的Intent-filter,寻找到满足属性的组件,当不止一个满足时, 会弹出一个让我们选择启动哪个的对话框。
举个例子:
比如我们在手机上修改头像,选择本地图片后,这时候系统自动跳转到图库,这个就是显示Intent(不好说,也许只有一个应用能响应该Intent)。
再例如,我们在手机上打开pdf文件,选择用其他应用打开,这时候就会弹出多个应用供我们选择,比如WPS、浏览器等,这个就是隐式Intent。
2.1 强制使用应用选择器
如果有多个应用响应隐式 Intent,则系统会弹出对话框,让用户选择要使用的应用,用户也可以将某个应用设置为该操作的默认选项。
如果想让用户无法选择用于操作的应用,而强制使用某个应用响应该操作时,可以使用如下代码:
1 |
|
2.2 Intent使用流程
三、Intent的七个属性
3.1 ComponentName(组件名称)
目标组件的名称。这是可选项,如果构建Intent时有组件名称,则为显式Intent,否则为隐式Intent。
组件名称由组件所在应用程序配置文件中设置的包名+组件的全限定类名组成。
例如:
1 |
|
ComponentName中的两个参数,第一个为包名,第二个为全限定类名。如果全限定类名的前面和包名是一样的话,是可以省略的,上面这个例子的组件名称可以写成com.example.demo/.DemoActivity。
3.2 Action(动作)
一个普通的字符串,代表Intent要完成的一个抽象动作,它告诉系统或其他组件要执行什么操作。
Action的两种类型
系统定义的Action
Android系统提供了许多预定义的Action,例如
- ACTION_VIEW
向用户展示某信息,比如使用浏览器打开网址,用图片应用显示图片等。 - ACTION_SEND
用于发送数据,比如电子邮件应用或者一些社交应用。 - ACTION_DIAL
显示带拨号盘的页面,让用户可以进行拨号动作。
更多Action常量详见通用 Intent | Android 开发者 | Android Developers (google.cn)
- ACTION_VIEW
应用程序定义的Action
开发者可以自定义Action,在应用程序内部使用,但确保加入应用的软件包名称作为前缀。
1
static final String ACTION_TIMETRAVEL = "com.example.action.SEND_EMAIL";
3.3 Category(类别)
一个普通的字符串,用于为Action提供额外的附加信息。一个Intent对象可以有多个Category。
Category的两种类型
系统定义的Category
CATEGORY_DEFAULT:默认类别,表示这个Intent没有特殊要求,可以被任何与之匹配的组件来处理。通常情况下,在一个Intent中只能添加一个CATEGORY_DEFAULT。
CATEGORY_BROWSABLE: 表示你的Activity能够打开浏览器并访问一个链接。如果你想创建一个能够响应http://或https://链接的Activity,你需要添加这个Category。
CATEGORY_LAUNCHER:表示该Activity是一个启动器(Launcher),也就是它会在设备上生成一个图标,当用户点击该图标时,系统会启动此Activity。在一个应用程序中,只有一个Activity可以拥有CATEGORY_LAUNCHER属性。
应用程序定义的Category
自定义的Category需要在AndroidManifest.xml配置文件中进行声明。例如:
1
<category android:name="com.example.myapp.MY_CUSTOM_CATEGORY" />
使用时代码如下:
1
2
3Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addCategory("com.example.myapp.MY_CUSTOM_CATEGORY");
startActivity(intent);这样,其他应用程序就可以使用该Intent Category来启动你的应用程序,并执行相关操作。
3.4 Data(数据), Type(MIME类型)
Data
Data通常用于向Action属性提供操作的数据,是一个URI对象,该URI可以表示文件、网址、数据库表格等不同类型的数据源。
Type
Type通常用于指定Data所指定的URI对应的MIME类型。
以下是一些常见的Android MIME类型(其中*是通配符,也可以指定某种类型):
- image/*:所有图像类型。
- audio/*:所有音频类型。
- video/*:所有视频类型。
- text/*:所有文本类型。
- application/pdf:PDF文件类型
- application/zip:Zip压缩文件类型
当然也可以自定义MIME类型。例如自定义json的MIME类型:
1 |
|
注意
若要同时设置 URI 和 MIME 类型,请勿调用 setData()
和 setType()
,因为它们会互相抵消彼此的值。请始终使用 setDataAndType()
同时设置 URI 和 MIME 类型。
原因参考:Android:关于Intents,为什么setType()和setData()会互相抵消?
3.5 Extra(额外信息)
Extra是一个键值对集合,用于传递额外信息。Extra的键是String类型,而值可以是任何Parcelable或Serializable对象(Java中用于序列化和反序列化对象的接口)。
3.6 Flag(标记)
充当 Intent 的元数据。指示 Android 系统如何启动 Activity(例如,Activity 应属于哪个任务),以及启动之后如何处理(例如,它是否属于最近的 Activity 列表)。
以下是几种常见的Flag:
- FLAG_ACTIVITY_NEW_TASK:创建一个新的任务栈来启动Activity,如果已经存在一个相同的任务栈,则直接将它调到前台。这个Flag通常用于从Service启动一个Activity。
- FLAG_ACTIVITY_CLEAR_TOP:清空Task栈顶部的所有Activity,使得目标Activity变成栈顶的Activity。如果该Activity不存在,则创建一个新的实例。这个Flag通常用于从深层次的Activity返回到栈顶Activity。
- FLAG_ACTIVITY_SINGLE_TOP:如果目标Activity已经位于栈顶,则不创建一个新的实例,而是继续使用原来的实例。这个Flag通常用于避免重复创建Activity的情况。
- FLAG_ACTIVITY_NO_HISTORY:启动一个Activity时不在Task栈中保存这个Activity的历史记录。这个Flag通常用于启动一个临时性的Activity。
- FLAG_ACTIVITY_CLEAR_TASK:清空整个Task栈,并且创建一个新的Task栈。这个Flag通常用于启动一个应用的主界面或者重新启动应用。
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:阻止启动的Activity出现在最近运行的应用列表中。
四、Intent过滤器
Intent过滤器(Intent-filter)是 AndroidMainifest.xml 配置文件中组件的子标签<intent-fileter>
。一个组件可以声明一个或者多个 Intent过滤器,只要其中一个通过匹配,该组件就可以响应相应 Intent。
Intent过滤器通过 Action、Category、Data 属性进行过滤,筛选出可以响应的组件。
例如官方文档中的一个例子:
1 |
|
可以看出ShareActivity
旨在发送图片、视频、文本。
4.1 Intent匹配规则
当收到隐式 Intent 以启动 Activity 时,系统会按照优先级(Action > Data > Category),将该 Intent 与 Intent 过滤器进行比较,搜索该 Intent 的最佳 Activity。
如果多个 Intent 过滤器都能够匹配到同一个 Intent,那么系统就会根据它们在清单文件中声明的顺序来决定使用哪一个 Intent 过滤器。在同一个 Intent 过滤器中,如果同时设置了多个 action 或 category,那么这些条件之间是 “或” 的关系,只要满足其中任意一个,该 Intent Filter 就会被匹配到。
Action匹配规则
- Intent只能设置一个action,但一个Intent过滤器可以声明多个action。只要Intent中的action与Intent过滤器的某一项action匹配,就可以通过action测试。
- 如果Intent过滤器未列出任何action,则 Intent 没有任何匹配项,因此所有 Intent 均无法通过action测试。
- 如果 Intent 未指定action,则只要Intent过滤器内包含至少一项action,就可以通过action测试。
Data匹配规则
Data的匹配规则分为两部分:URI和MIME。
URI匹配规则
URI的格式为<scheme>://<host>:<port>/<path>
,上述每个属性均为可选,但存在线性依赖关系:
- 如果未指定scheme,则会忽略host。
- 如果未指定host,则会忽略port。
- 如果未指定scheme和host,则会忽略path
Intent 中的 URI 与过滤器中的 URI 进行比较时,它仅与过滤器中包含的部分 URI 进行比较(即刚才所讲到的依赖关系)。例如:
- 如果过滤器仅指定scheme,则具有该scheme的所有 URI 均与该过滤器匹配。
- 如果过滤器指定scheme和authority,但未指定path,则具有相同scheme和authority的所有 URI 都会通过过滤器,无论其path如何均是如此。
- 如果过滤器指定scheme、authority和path,则仅具有相同scheme、authority和path的 URI 才会通过过滤器。
- path 部分可以使用通配符(*),仅需部分匹配路径名即可。
URI和MIME匹配规则
- 仅当过滤器未指定任何 URI 或 MIME 类型时,不含 URI 和 MIME 类型的 Intent 才会通过测试。
- 如果 Intent 指定 URI 或 MIME 类型,而过滤器中没有声明data元素,则Intent不能通过测试。
- 如果 Intent 指定 URI 或 MIME 类型,仅当有过滤器能匹配其URI和MIME类型时,才会通过测试。
- 对于包含 URI 但不含 MIME 类型(既未显式声明,也无法通过 URI 推断得出)的 Intent,仅当其 URI 与过滤器的 URI 格式匹配、且过滤器同样未指定 MIME 类型时,才会通过测试。
- 仅当过滤器列出相同的 MIME 类型且未指定 URI 格式时,包含 MIME 类型但不含 URI 的 Intent 才会通过测试。
- 如果过滤器只是列出 MIME 类型,则默认支持URI含
content:
或file:
的数据。
由于大部分可用数据均由内容提供程序分发,因此指定数据类型(而非 URI)的过滤器也许最为常见。
另一常见的配置是具有架构和数据类型的过滤器,如:
1 |
|
Category匹配规则
- Intent 可以包含多项 category,Intent 过滤器也可以包含多项 category。Intent 中的每项category 必须在Intent过滤器中都有对应项,才能通过category 测试。(Intent过滤器声明的category可以超过Intent中的category数量)
- 不含category的Intent始终会通过Intent过滤器中的category测试。
- 当目标组件为 Activity 时,如需响应隐式 Intent,必须添加 “android.intent.category.DEFAULT” 到 Intent 过滤器中。
- 目标组件为广播时,Intent 和 Intent 过滤器都不设置 category,可通过匹配。
五、Intent数据传递
Intent不仅可以用来启动组件,还可以用来传递数据。
Intent传送数据是以键值对的形式。
传递单个数据时,主要通过putExtra()
方法,该方法接收两个参数,第一个是数据的键,第二个是数据的值。
传递多个数据时,使用Bundle对象作为容器,先将数据存储到Bundle中,然后通过putExtras()
方法传入Intent中。
Intent传递的数据可分为以下几种:
- 基本类型数据:如int、float、double等。
- 字符串类型数据:如String、CharSequence等。
- 序列化数据类型:如ArrayList、Bundle等。
- 自定义数据类型:如Parcelable、Serializable对象。
5.1 Intent数据回传
一个Activity可以通过Intent启动另一个Activity,有时候,我们希望后者能够返回一些数据给前者,这时候就需要使用数据回传。典型的应用场景如:充值。
数据回传的过程如下:
- 在调用者 Activity 中,重写
onActivityResult(int requestCode, int resultCode, Intent intent)
方法。其中requestCode
是请求码,resultCode
是结果码。 - 在调用者 Activity 中,使用
startActivityForResult(Intent intent, int requestCode)
方法启动另一个 Activity。其中请求码由我们自己设定,用于区分请求来源。 - 在被调用的 Activity 中,获取需要返回的数据,并将其放入一个 Intent 对象中。
- 在被调用的 Activity 中,调用
setResult(int resultCode, Intent data)
方法来设置返回结果和结果码。 - 在被调用的 Activity 中,调用
finish()
方法来结束当前 Activity 的生命周期。当被请求的 Activity 被关闭时,就会调用请求者Activity的onActivityResult()
方法。
请求码和结果码的作用如下:
- 区分不同的请求:如果一个 Activity 发送了多个不同的请求,在 onActivityResult 方法中使用请求码来区别不同的请求,以便做出不同的处理。
- 标识操作执行结果:结果码用于标识操作的执行结果,比如成功、失败、用户取消等。这可以告诉调用方执行的操作是否成功。
- 保证数据正确性:使用请求码和结果码可以确保返回的数据来自于正确的 Activity,防止数据被恶意篡改或者被其他程序错误地返回。
- 简化代码编写:使用请求码和结果码,开发者可以更加简单方便地实现 Activity 之间的数据传递,减少编写冗长代码的工作量。
六、待定Intent
待定Intent(Pending Intent)是Android平台中的一种特殊类型的Intent对象,通常被用于延迟执行某个动作。待定Intent和普通Intent的区别在于,待定Intent是在稍有的实践点由系统代理执行,而不是立刻执行。
典型的应用场景如:设置闹钟、备忘录。
6.1 PendingIntent的相关方法
- getActivity():返回一个启动 Activity 的 PendingIntent。
- getService():返回一个启动 Service 的 PendingIntent。
- getBroadcast():返回一个发送广播的 PendingIntent。
- cancel():取消当前的 PendingIntent。
- send():发送当前的 PendingIntent。
6.2 PendingIntent的标志位
在创建待定 Intent 时,我们还可以设置 PendingIntent.FLAG_XXX等标志位。这些标志位主要用于控制 PendingIntent 的一些行为和属性,以下是几种常用的标志位:
- FLAG_ONE_SHOT:表示 PendingIntent 只能被使用一次。
- FLAG_CANCEL_CURRENT:表示如果 PendingIntent 已经存在,则会先取消当前的 PendingIntent,然后重新创建一个新的 PendingIntent。
- FLAG_UPDATE_CURRENT:表示如果 PendingIntent 已经存在,则会更新它的 Extras 数据,而不是重新创建一个新的 PendingIntent。
- FLAG_NO_CREATE:表示如果 PendingIntent 已经存在,返回它,否则返回 null。
参考:
Intent详解以及Activity的跳转与数据传递 - 知乎 (zhihu.com)
4.5.1 Intent的基本使用 | 菜鸟教程 (runoob.com)
Intent 和 Intent 过滤器 | Android 开发者 | Android Developers (google.cn)