Android 启动 Activity 分为两种,显式调用和隐式调用,显式调用需要明确地指定被启动对象的组件信息,包括包名和类名,而隐式调用则不需要明确指定组件信息。原则上一个 Intent 不应该即是显式调用又是隐式调用,如果二者共存的化,以显式调用为主。
显式调用
显式调用代码如下。
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);
隐式调用
隐式调用需要 Intent 能够匹配目标组件的 IntentFilter 所设置的过滤信息,如果不匹配将无法启动目前Activity。IntentFilter 中的过滤信息有 action(动作)、category(类别)、data。
为了匹配过滤列表,需要同时匹配过滤列表中的 action、category、data 信息,否则匹配失败。一个过滤列表中的 action、category 和 data 可以有多个,所有的 action、category、Data 分别构成不同类别,同一类别的信息共同约束当前类别的匹配过程。只有一个 Intent 同时匹配 action 类别、category 类别、data 类别才算完全匹配,只有完全匹配才能成功启动目标 Activity。一个 Activity 中可以有多个 intent-filter,一个 Intent 只要能匹配任何一组 intent-filter 就能成功启动对应的 Activity。
action 的匹配规则
action 是一个字符串,系统预定义了一些 action,同时我们也可以自己在应用中定义自己的 action。一个 intent-filter 里可以有多个 action,只要 Intent 中的 action 能够和过滤规则中的任何一个 action 相同即可匹配成功。action 区分大小写。
category 的匹配规则
category 是一个字符串,系统预定义了一些 category,同时我们也可以自己在应用中定义自己的 category。要求 Intent 如果含有 category ,那么所有的 category 都category和过滤规则中的其中一个 category 相同。系统在调用 startActivity 或者 staActivityForResult 都会默认加上 "android.intent.category.DEFAULT",所以必须在 intent-filter 加上 "android.intent.category.DEFAULT" 这个 category。
Intent intent = new Intent();
intent.setAction("com.test.second");
intent.addCategory("com.test.second.category");
startActivity(intent);
AnroidManifest.xml
data 的匹配规则
data 的匹配规则和 action 类似,如果过滤规则中定义了 data,那么 Intent 必须要定义可匹配的 data。
data 由两部分组成,mimeType 和 URI。mimeType 指媒体类型,比如 image/jpeg、text/plaind、video/* 等等,可以表示图片、文本、视频等不同的媒体格式。
URI中的结构如下。
//:/[ | | ]
scheme:URI 的模式,比如 http、file、content 等,如果 URI 没有指定 scheme,那么整个 URI 的其他参数都无效,意味着 URI 是无效的。
host:URI 的主机号,如 www.xxx.com,如果 host 未指定,那么整个 URI 中的其他参数无效, 意味着 URI 是无效的。
port:URI 中的端口号,比如 80,仅当 URI 中指定了 scheme 和 host 参数的时候 port 参数才是有意义的。
path、pathPrefix 和 pathPattern:表示路径信息,path 表示完整的路径信息,pathPrefix 表示路径的前缀信息,pathPattern 表示完整的路径信息,但是它里面可以包含通配符 , 表示 0 个或多个任意字符,遵循正则表达式的规范,如果想要表示真实的字符串,* 要写成 \\*,\ 要写成 \\\\。
- 情况一:data 规则不完整
这种规则指定了媒体类型为所有类型的图片,那么 Intent 中的 mimeType 属性必须与 "image/*" 相匹配,这种情况下虽然过滤规则没有指定 URI,但是却有默认值,URI 的默认值为 content 和 file,所以 Intent 中的 URI 部分的 schema 必须为 content 或者 file 才能匹配。如下示例:
intent.setDataAndType(Uri.parse("file://abc"), "image/png");
如果要为 Intent 指定完整的 data,必须要调用 setDataAndType,不能先调用 setData 再调用 setType,因为这两个方法会彼此清除对方的值。
public @NonNull Intent setData(@Nullable Uri data) {
mData = data;
mType = null;
return this;
}
public @NonNull Intent setType(@Nullable String type) {
mData = null;
mType = type;
return this;
}
- 情况二:定义了多组data规则,并且每个data都定义了完整属性,既有URI又有mimeType。
intent.setDataAndType(Uri.parse("http://www.baidu.com"), "video/mpeg");
通过隐式调用方式启动一个 Activity 的时候,可以做以下判断,看是否有 Activity 能够匹配隐式 Intent,如果不做判断就有可能出现错误。判断方法有两种:一是采用PackageManager的resolveActivity方法,二是采用Intent的resolveActivity方法,如果他们找不到匹配的Activity就会返回null。
Intent intent = new Intent();
intent.setAction("com.test.second");
intent.setDataAndType(Uri.parse("http://www.baidu.com"), "video/mpeg");
ComponentName componentName = intent.resolveActivity(getPackageManager());
if (componentName == null) {
Toast.makeText(this, "匹配出错", Toast.LENGTH_SHORT).show();
} else {
startActivity(intent);
}