Android Intent

一、概述

Intent是Android四大组件通信桥梁,它负责在组件之间传递消息和数据,是一个消息传递对象。通过使用Intent,可以启动另一个活动Activity、服务Service、广播接收器BroadcastReceiver,以及发送和接收数据。

二、Intent的两种类型

显式Intent多用来实现一个应用内部的跳转,而隐式intent多用来实现应用与应用之间的跳转

  1. 显示Intent

    明确指定了要启动的目标组件的名称。

    值得注意的是从Android 5.0开始, Service 只能通过显式 Intent 启动,如果使用隐式 Intent 调用 bindService(),系统会引发异常。因为使用隐式 Intent 启动服务存在安全隐患,无法确定哪些服务将响应 Intent,且用户无法看到哪些服务已启动。

  2. 隐式Intent

    不指定组件名,而是指定了可处理该Intent的某些组件的属性,如Action、Data、Category等,然后系统会根据这些属性去匹配AndroidManifest.xml相关组件的Intent-filter,寻找到满足属性的组件,当不止一个满足时, 会弹出一个让我们选择启动哪个的对话框。

举个例子:

比如我们在手机上修改头像,选择本地图片后,这时候系统自动跳转到图库,这个就是显示Intent(不好说,也许只有一个应用能响应该Intent)。

再例如,我们在手机上打开pdf文件,选择用其他应用打开,这时候就会弹出多个应用供我们选择,比如WPS、浏览器等,这个就是隐式Intent。

2.1 强制使用应用选择器

如果有多个应用响应隐式 Intent,则系统会弹出对话框,让用户选择要使用的应用,用户也可以将某个应用设置为该操作的默认选项。

如果想让用户无法选择用于操作的应用,而强制使用某个应用响应该操作时,可以使用如下代码:

1
2
3
4
5
6
7
8
9
Intent sendIntent = new Intent(Intent.ACTION_SEND);
Intent intent=Intent.createChooser(viewIntent,"应用选择器");
//createChooser()创建显式调用选择器,用户将无法设定默认选项
Intent chooser = Intent.createChooser(sendIntent, title);

// 判断该Intent是否存在可响应Activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(chooser);
}

2.2 Intent使用流程

三、Intent的七个属性

3.1 ComponentName(组件名称)

目标组件的名称。这是可选项,如果构建Intent时有组件名称,则为显式Intent,否则为隐式Intent。

组件名称由组件所在应用程序配置文件中设置的包名+组件的全限定类名组成。

例如:

1
2
3
4
ComponentName componentName = new ComponentName("com.example.demo","com.example.demo.DemoActivity");
Intent intent = new Intent();
intent.setComponent(componentName);
startActivity(intent);

ComponentName中的两个参数,第一个为包名,第二个为全限定类名。如果全限定类名的前面和包名是一样的话,是可以省略的,上面这个例子的组件名称可以写成com.example.demo/.DemoActivity。

3.2 Action(动作)

一个普通的字符串,代表Intent要完成的一个抽象动作,它告诉系统或其他组件要执行什么操作。

Action的两种类型

  1. 系统定义的Action

    Android系统提供了许多预定义的Action,例如

    • ACTION_VIEW
      向用户展示某信息,比如使用浏览器打开网址,用图片应用显示图片等。
    • ACTION_SEND
      用于发送数据,比如电子邮件应用或者一些社交应用。
    • ACTION_DIAL
      显示带拨号盘的页面,让用户可以进行拨号动作。

    更多Action常量详见通用 Intent | Android 开发者 | Android Developers (google.cn)

  2. 应用程序定义的Action

    开发者可以自定义Action,在应用程序内部使用,但确保加入应用的软件包名称作为前缀。

    1
    static final String ACTION_TIMETRAVEL = "com.example.action.SEND_EMAIL";

3.3 Category(类别)

一个普通的字符串,用于为Action提供额外的附加信息。一个Intent对象可以有多个Category。

Category的两种类型

  1. 系统定义的Category

    • CATEGORY_DEFAULT:默认类别,表示这个Intent没有特殊要求,可以被任何与之匹配的组件来处理。通常情况下,在一个Intent中只能添加一个CATEGORY_DEFAULT。

    • CATEGORY_BROWSABLE: 表示你的Activity能够打开浏览器并访问一个链接。如果你想创建一个能够响应http://或https://链接的Activity,你需要添加这个Category。

    • CATEGORY_LAUNCHER:表示该Activity是一个启动器(Launcher),也就是它会在设备上生成一个图标,当用户点击该图标时,系统会启动此Activity。在一个应用程序中,只有一个Activity可以拥有CATEGORY_LAUNCHER属性。

  2. 应用程序定义的Category

    自定义的Category需要在AndroidManifest.xml配置文件中进行声明。例如:

    1
    <category android:name="com.example.myapp.MY_CUSTOM_CATEGORY" />

    使用时代码如下:

    1
    2
    3
    Intent 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
application/json

注意

若要同时设置 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<activity android:name="ShareActivity">
<!-- This activity handles "SEND" actions with text data -->
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
<!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.SEND_MULTIPLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="application/vnd.google.panorama360+jpg"/>
<data android:mimeType="image/*"/>
<data android:mimeType="video/*"/>
</intent-filter>
</activity>

可以看出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匹配规则

  1. 仅当过滤器未指定任何 URI 或 MIME 类型时,不含 URI 和 MIME 类型的 Intent 才会通过测试。
  2. 如果 Intent 指定 URI 或 MIME 类型,而过滤器中没有声明data元素,则Intent不能通过测试。
  3. 如果 Intent 指定 URI 或 MIME 类型,仅当有过滤器能匹配其URI和MIME类型时,才会通过测试。
  4. 对于包含 URI 但不含 MIME 类型(既未显式声明,也无法通过 URI 推断得出)的 Intent,仅当其 URI 与过滤器的 URI 格式匹配、且过滤器同样未指定 MIME 类型时,才会通过测试。
  5. 仅当过滤器列出相同的 MIME 类型且未指定 URI 格式时,包含 MIME 类型但不含 URI 的 Intent 才会通过测试。
  6. 如果过滤器只是列出 MIME 类型,则默认支持URI含 content:file:的数据。

由于大部分可用数据均由内容提供程序分发,因此指定数据类型(而非 URI)的过滤器也许最为常见。

另一常见的配置是具有架构和数据类型的过滤器,如:

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

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传递的数据可分为以下几种:

  1. 基本类型数据:如int、float、double等。
  2. 字符串类型数据:如String、CharSequence等。
  3. 序列化数据类型:如ArrayList、Bundle等。
  4. 自定义数据类型:如Parcelable、Serializable对象。

5.1 Intent数据回传

一个Activity可以通过Intent启动另一个Activity,有时候,我们希望后者能够返回一些数据给前者,这时候就需要使用数据回传。典型的应用场景如:充值。

数据回传的过程如下:

  1. 在调用者 Activity 中,重写 onActivityResult(int requestCode, int resultCode, Intent intent) 方法。其中requestCode是请求码,resultCode是结果码。
  2. 在调用者 Activity 中,使用 startActivityForResult(Intent intent, int requestCode) 方法启动另一个 Activity。其中请求码由我们自己设定,用于区分请求来源。
  3. 在被调用的 Activity 中,获取需要返回的数据,并将其放入一个 Intent 对象中。
  4. 在被调用的 Activity 中,调用 setResult(int resultCode, Intent data) 方法来设置返回结果和结果码。
  5. 在被调用的 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)

Android Intent详解 - 简书 (jianshu.com)

Android-Intent-Intent Filter篇 - 知乎 (zhihu.com)


Android Intent
http://example.com/2023/07/04/Android安全/Android Intent/
作者
gla2xy
发布于
2023年7月4日
许可协议