Intent是一个消息传递对象,可以使用它向其他组件做一些请求操作。
尽管 Intent 可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个:
- 启动 Activity
- 启动Service
- 传递广播
Intent 分为两种:
- 显示Intent:知道明确的类名。按照类名来做相应的操作。
- 隐式Intent:不知道明确的类。但是知道一些Action等,通过意图过滤器匹配符合的组件。
显示Intent
显示的Intent 比较简单。直接通过类名做相应操作即可,显式 Intent 启动 Activity 或服务时,系统将立即启动对象中指定的应用组件:
// 启动Activity
startActivity(new Intent(this, OtherActivity.class));
// 绑定Service
Intent intent = new Intent(this, BookService.class);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
...
什么样的Intent属于显示的Intent呢?
Intent有个很重要的属性: 组件名称
区分Intent是显示还是隐式的,就是你是否传递了** 组件名称 **。
- 对应在 **Manifest ** 中就是 ** android:name=".act.MainActivity" **。
- 对应在Java中就是** ComponentName** 这个类。
如果打开 **new Intent (Context, Activity.class) **的源码:
public Intent(Context packageContext, Class<?> cls) {
mComponent = new ComponentName(packageContext, cls);
}
代码就一行,就是根据传递的Context和Class生成** ComponentName** 。
因此,只要指定了** ComponentName** 对象,就属于显示启动。
所以以下代码也是属于显示启动:
Intent intent = new Intent();
// com.demo 是应用包名,在Manifest的package中查看
// com.demo.MainActivity 是要启动的组件的类名
intent.setComponent(new ComponentName("com.demo","com.demo.MainActivity"));
startActivity(intent);
注意1: *如果Activity不是入口Activity或者是一个Service组件,想要被其他应用访问,需要加上属性 android:exported="true" *
注意2: *Service尽量使用显示启动。为了确保应用的安全性,从 Android 5.0(API 级别 21)开始,如果使用隐式 Intent 调用 bindService(),系统会引发异常。 *
隐式Intent
如果没有指定 ComponentName 对象的Intent就属于隐式意图。
隐式的Intent 就比较复杂了,需要了解 Intent-Filter 的匹配规则。
intent-filter 中包含一下几个内容:
- <action> 操作
- <data> 数据
- <category> 类别
使用隐式 Intent 时,系统通过将 Intent 中的action、data、category 与 intent-filter 的内容进行匹配,只有action、data、category这三部分都匹配成功,这个intent-filter才算匹配成功。
这时有三种情况:
- 如果 Intent 与 intent-filter匹配,则系统将启动该组件,并向其传递 Intent对象。
- 如果多个 Intent 过滤器匹配的话,则系统会显示一个对话框,让用户选取相应的应用。
- ** 如果匹配不成功,则调用将会失败,且应用会崩溃。**
为了程序的健壮,需要使用Intent 对象的 **resolveActivity() ** 做一个检测。如果结果为非空,则至少有一个应用能够处理该 Intent,且可以安全调用 startActivity()。 如果结果为空,则不应使用该 Intent,否则会引发异常。
匹配是要将代码中的Intent 与 Manifest中配置的Intent-Filter进行匹配
action匹配规则
Intent可以设置多个Action,** intent-filter **也可以设置多个Action。
匹配规则:
- Intent中必须至少有一个Action,并且Intent中的Action必须都要在** intent-filter **中存在
category匹配规则
category与Action规则类似,但是有点不同:
- 在intent中可以不配置category,但是只要你配置了,不管配置几个,都必须在Intent-Filter中找到相同项。
注意:
在 startActivity 或者 startActivityForResult 两个方法中,系统会默认为** Intent 加上一个 android.intent.category.DEFAULT 属性。所以为了我们定义的组件可以隐式启动,我们都必须在 intent-filter 中配置上这个,否则 category **无法匹配成功。
data匹配规则
data的匹配规则和前面也是类似的,只是data结构稍微复杂一些。
- 只要在Intent-Filter中存在data,那么Intent中必须Intent-Filter中的一个data,才能成功。
data由一个 URI 和 一个 mimeType组成 。
在data标签中可以只有URI 或者mimeType ,也可以两者同时存在。
data的两个属性介绍:
- mimeType 比较简单,类似 text/plain、***image/**** 等。
-
URI 比较复杂,结构是:
<scheme>://<host>:<port>/<path>
URI例子:content://com.example.project:200/folder/subfolder/etc
- scheme: content
- host: com.example.project
- port: 200
- path: folder/subfolder/etc
对应到Intent-Filter中就是这几个属性:
<data android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:mimeType="string" />
这几个属性都是可选的,但是,如果没有指定scheme,那么这个URI就是无效的。
URI的scheme 有两个默认值,content 和 file。当data标签中只有mimeType时,我们可以使用content 和 file去匹配。
同时下面两种写法是一样的效果:
// 写法1
<intent-filter>
<data android:host="www.baidu.com"
android:port="8888"
android:scheme="http" />
</intent-filter>
// 写法2
<intent-filter>
<data android:scheme="http"/>
<data android:host="www.baidu.com"/>
<data android:port="8888" />
</intent-filter>
同时 Intent.setData() 和 Intent.setType() ,会相互清空,所以要同时设置data和type需要使用 Intent.setDataAndType()方法。
总结:
想要隐式Intent匹配成功,action和data必须与Intent-Filter匹配,category传空也是ok的。
若Action有多个,匹配一个即可。
若想要传category,传的category,必须是相匹配的。