概述
Intent翻译为意图,是android中Activity,Service,BroadcastReciver三大组件之间的信使,负责开启它们并可以传递信息,即消息传递对象,传递的信息可以是基本数据类型,也可以是一个对象。
- 启动Activity:startActivity();startActivityForResult(); onActivityResult();
- 启动Service:startService();或bindService();
- 启动BroadcastReceiver:sendBroadcast();sendOrderedBroadcast();sendStickyBroadcast();
类型
- 显式Intent:显式地指定要启动的组件名称,通常用于启动自己的应用中的组件,如启动service在后台下载文件
- 隐式Intent:不声明要启动的组件名称,而是通过传入一段代表某种操作的字符串,从而允许能执行该种操作的组件启动并接受它,通常是其他应用中的组件
[^footnote]从api21开始,如果使用隐式Intent调用bindService,系统会抛出异常
注意
为了避免无意中运行不同应用的 Service,请始终使用显式 Intent 启动您自己的服务,且不必为该服务声明 Intent 过滤器。
过滤器示例
构造Intent
Intent携带了android系统用来确定要启动哪个组件的信息
组件名
这是可选的,如果有,则Intent必定是显式的,没有则是隐式的,如果是开启service,为了确保应用的安全性,务必使用显式(隐式有可能开启其他应用的service)
操作 action
一个声明常规操作的字符串,系统定义了一些常用操作,也可以自定义
数据 data
引用待操作数据和/或数据mime类型的Uri(URI对象)提供的数据类型通常由该Intent的操作决定
有时候,隐式创建Intent的时候,仅仅凭借操作和数据不能准确地启动正确的组件,比如播放音频结果却启动了显示图像的activity,因为URI格式可能十分相似。所以,指定数据的MIME类型也很重要(不过有时候,MIME类型可以从URI中看出)
要仅设置数据 URI,请调用 setData()。要仅设置 MIME 类型,请调用 setType()。如有必要,您可以使用 setDataAndType() 同时显式设置二者。
类别 category
一个包含应处理 Intent 组件类型的附加信息的字符串。您可以将任意数量的类别(可能有多个或者一个都没有)描述放入一个 Intent 中,但大多数 Intent 均不需要类别。
- CATEGORY_BROWSABLE
目标 Activity 允许本身通过 Web 浏览器启动,以显示链接引用的数据,如图像或电子邮件。
- CATEGORY_LAUNCHER
该 Activity 是任务的初始 Activity,在系统的应用启动器中列出
通过intent.addCategory() 向Intent中指定类别
以上列出的这些属性(组件名称、操作、数据和类别)表示 Intent 的既定特征。通过读取这些属性,Android 系统能够解析应当启动哪个应用组件。
除了携带既定特征的信息,Intent还可以携带不影响其解析的信息:Extra Flags
Exra 额外数据
携带完成请求的附加信息的健值对,或者Bundle对象,然后使用putExtra()或者putExtras()放入intent中
例如,使用 ACTION_SEND 创建用于发送电子邮件的 Intent 时,可以使用 EXTRA_EMAIL 键指定“目标”收件人,并使用 EXTRA_SUBJECT 键指定“主题”,之后一旦有intent想要访问该组件,则必须携带除动作外的extra,并将健设置为EXTRA_EMAIL
注*Intent 类将为标准化的数据类型指定多个 EXTRA_ 常量。如需声明自己的附加数据 键(对于应用接收的 Intent ),请确保将应用的软件包名称作为前缀。例如:
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
Flags 标志
标示Intent该如何启动activity,Receiver,以及启动后如何处理,其值在Intent类中定义,充当Intent的元数据
示例
显式Intent示例
Intent downloadIntent = new Intent(this, DownloadService.class);
// fileUrl是网络中的一个url地址,通过parse方法转化成URI
//比如"http://www.example.com/image.png"
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);
隐式Intent示例
//创建一个string类型的文本
Intent sendIntent = new Intent();
//设置操作/动作 action
sendIntent.setAction(Intent.ACTION_SEND);
//设置额外数据 extra,指定其类型为Intent.EXTRA_TEXT
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
//设置类型 type
sendIntent.setType("text/plain");
// 用户可能没有任何应用处理你发送到 startActivity() 的隐式 Intent。如果出现这种情况,则调用将会失败,且应用会崩溃。使用如下方式判断是否有组件接受你的intent
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
//上面构造的intent,没有设置data,而是通过声明了Intent的数据类型,指定了Extra所携带的内容类型
强制应用选择器
说明:当有多个组件响应隐式intent的时候,系统会淡出一个选择列表框让用户选择一个,默认情况下,列表框可以让用户选择该intent的默认启动组件,下次再次启动时,就不会弹出对话框了。如果要让用户无法为该操作选择默认启动组件的话,则可以使用强制应用选择器。
使用方法: 使用createChooser()包装Intent
示例
Intent sendIntent = new Intent(Intent.ACTION_SEND);
//选择器的标题,就像“选择如下的应用分享您的图片”
String title = getResources().getString(R.string.chooser_title);
Intent chooser = Intent.createChooser(sendIntent, title);
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(chooser);
}
接受隐式Intent
对于接受方来说,要公布本组件可以接受哪些隐式intent,应在清单文件中为该组件设置意图过滤器(intent filter)可以设置多个,它根据Intent的操作(action),数据(data),类别(category)指定自己所接受的intent类型,仅当隐式intent至少满足一个过滤器时,才能让接受者接受。
注意:显式 Intent 始终会传递给其目标,无视目标的意图过滤器
应用组件应当为每个单独的功能/作业设置各自的过滤器,比如一个activity有查看图像和编辑图像两种功能,当组件启动时,他将检查intent并根据intent里面的信息决定其具体的行为(是否显示编辑器)
例如,以下是一个使用 Intent 过滤器进行的 Activity 声明,当数据类型为文本时,系统将接收 ACTION_SEND Intent :
<activity android:name="ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
限制对组件的访问
使用 Intent 过滤器时,无法安全地防止其他应用启动组件。尽管 Intent 过滤器将组件限制为仅响应特定类型的隐式 Intent,但如果开发者确定您的组件名称,则其他应用有可能通过使用显式 Intent 启动您的应用组件。如果必须确保只有您自己的应用才能启动您的某一组件,请针对该组件将 exported 属性设置为 “false”。
IntentFilter 示例:
<activity android:name="MainActivity">
<!-- 第一个 Activity MainActivity 是应用的主要入口点。当用户最初使用启动器图标启动应用时,该 Activity 将打开:
ACTION_MAIN 操作指示这是主要入口点,且不要求输入任何 Intent 数据。CATEGORY_LAUNCHER 类别指示此 Activity 的图标应放入系统的应用启动器。如果 <activity> 元素未使用 icon 指定图标,则系统将使用 <application> 元素中的图标。 -->
<!--这两个元素必须配对使用,Activity 才会显示在应用启动器中。-->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="ShareActivity">
<!-- 第二个 Activity ShareActivity 旨在便于共享文本和媒体内容。尽管用户可以通过从 MainActivity 导航进入此 Activity,但也可以从发出隐式 Intent(与两个 Intent 过滤器之一匹配)的另一应用中直接进入 ShareActivity。-->
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
<!-- 多类型的共享文本和媒体内容的过滤器 -->
<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>
使用PendingIntent,待定Intent
Intent解析
系统通过将 Intent 与所有这三个元素进行比较,根据过滤器测试隐式 Intent,suo隐式 Intent 若要传递给组件,必须通过所有这三项测试。如果 Intent 甚至无法匹配其中任何一项测试,则 Android 系统不会将其传递给组件。
- Intent操作
- Intent数据(URI和数据类型)
- Intent类别
操作测试
<!--既可以写多个,也可以一个不写-->
<intent-filter>
<action android:name="android.intent.action.EDIT" />
<action android:name="android.intent.action.VIEW" />
...
</intent-filter>
类别测试
<!--既可以写多个,也可以一个不写-->
<intent-filter>
<!--但是,如果这是activity的过滤器,要想让其他组件通过startActivity或者startActivityForReustl启动,则必须设置它为category.DEFAULT,因为Android 会自动将 CATEGORY_DEFAULT 类别应用于传递给 startActivity() 和 startActivityForResult() 的所有隐式 Intent-->
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
...
</intent-filter>
数据测试
<intent-filter>
<data android:mimeType="video/mpeg" android:scheme="http" ... />
<data android:mimeType="audio/mpeg" android:scheme="http" ... />
...
</intent-filter
每个 <data> 元素均可指定 URI 结构和数据类型(MIME 介质类型)。URI 的每个部分均包含单独的 scheme、host、port 和 path 属性:
<scheme>//:<host>:<port>/<path>
例如:content://com.example.project:200/folder/subfolder/etc
在此 URI 中,架构是 content,主机是 com.example.project,端口是 200,路径是 folder/subfolder/etc
在 “data”元素中,上述每个属性均为可选,但存在线性依赖关系:
- 如果未指定架构,则会忽略主机。
- 如果未指定主机,则会忽略端口。
- 如果未指定架构和主机,则会忽略路径。
将 Intent 中的 URI 与过滤器中的 URI 规范进行比较时,它仅与过滤器中包含的部分 URI 进行比较。例如:
如果过滤器仅指定架构,则具有该架构的所有 URI 均与该过滤器匹配。
如果过滤器指定架构和权限、但未指定路径,则具有相同架构和权限的所有 URI 都会通过过滤器,无论其路径如何均是如此。
如果过滤器指定架构、权限和路径,则仅具有相同架构、权限和路径 的 URI 才会通过过滤器。
注意:路径规范可以包含星号通配符 (),因此仅需部分匹配路径名即可。*
数据测试会将 Intent 中的 URI 和 MIME 类型与过滤器中指定的 URI 和 MIME 类型进行比较。规则如下:
A:仅当过滤器未指定任何 URI 或 MIME 类型时,不含 URI 和 MIME 类型的 Intent 才会通过测试。
B:对于包含 URI、但不含 MIME 类型(既未显式声明,也无法通过 URI 推断得出)的 Intent,仅当其 URI 与过滤器的 URI 格式匹配、且过滤器同样未指定 MIME 类型时,才会通过测试。
C:仅当过滤器列出相同的 MIME 类型且未指定 URI 格式时,包含 MIME 类型、但不含 URI 的 Intent 才会通过测试。
D:仅当 MIME 类型与过滤器中列出的类型匹配时,包含 URI 和 MIME 类型(通过显式声明,或可以通过 URI 推断得出)的 Intent 才会通过测试的 MIME 类型部分。如果 Intent 的 URI 与过滤器中的 URI 匹配,或者如果 Intent 具有 content: 或 file: URI 且过滤器未指定 URI,则 Intent 会通过测试的 URI 部分。换而言之,如果过滤器仅列出 MIME 类型,则假定组件支持 content: 和 file: 数据。
最后一条规则,即规则 (d),反映了期望组件能够从文件中或内容提供商处获得本地数据。因此,其过滤器可以仅列出数据类型,而不必显式命名 content: 和 file: 架构。这是一个典型的案例。例如,下文中的 <data> 元素向 Android 指出,组件可从内容提供商处获得并显示图像数据:
<intent-filter>
<data android:mimeType="image/*" />
...
</intent-filter>
由于大部分可用数据均由内容提供商分发,因此指定数据类型(而非 URI)的过滤器也许最为常见。
另一常见的配置是具有架构和数据类型的过滤器。例如,下文中的 元素向 Android 指出,组件可从网络中检索视频数据以执行操作:
<intent-filter>
<data android:scheme="http" android:type="video/*" />
...
</intent-filter>
Intent匹配
主页应用通过使用指定 ACTION_MAIN 操作和 CATEGORY_LAUNCHER 类别的 Intent 过滤器查找所有 Activity,以此填充应用启动器。
PackageManager 提供了一整套 query…() 方法来返回所有能够接受特定 Intent 的组件。此外,它还提供了一系列类似的 resolve…() 方法来确定响应 Intent 的最佳组件。例如,queryIntentActivities() 将返回能够执行那些作为参数传递的 Intent 的所有 Activity 列表,而 queryIntentServices() 则可返回类似的服务列表。这两种方法均不会激活组件,而只是列出能够响应的组件。对于广播接收器,有一种类似的方法: queryBroadcastReceivers()。
注:整理自谷歌开发者文档
Intent的谷歌官方api链接