1. IntentFilter 的匹配规则
启动 Activity 分为两种:显式调用和隐式调用,分别对应显式 Intent 和隐式 Intent,具体来说,分别对应 Intent 的不同的重载方法
- 隐式调用需要 Intent 能够匹配目标 Activity 的 intent-filter 标签中所设置的过滤信息,如果不匹配将不能启动目标 Activity
- IntentFilter 中的过滤信息有 action、category、data,这三个需要同时匹配才能启动成功
一个 IntentFilter 过滤列表中的 action、category 和 data 可以有多个,所有的 action、category、data 分别构成不同类别,同一类别的信息共同约束当前类别的匹配过程
- 只有一个 Intent 同时匹配 action 类别、category 类别、data 类别才算完全匹配,只有完全匹配才能成功启动目标 Activity
- 一个 Activity 中可以有多个 intent-filter,一个 Intent 只要能匹配任何一组 intent-filter 即可成功启动对应的 Activity
2. action 的匹配规则
- action 是一个字符串,系统预定义了一些 action,同时我们也可以在应用中定义自己的 action
- action 的匹配规则是 Intent 中的 action 必须能够和过滤规则中的 action 匹配,这里说的匹配是指 action 的字符串值完全一样
- 一个过滤规则中可以有多个 action,那么只要 Intent 中的 action 能够和过滤规则中的任何一个 action 相同即可匹配成功
- action 的匹配要求 Intent 中的 action 存在(重载的隐式 Intent 的构造方法参数不为空)且必须和过滤规则中的其中一个 action 相同
- 另外,action 区分大小写,大小写不同字符串相同的 action 会匹配失败
3. category 的匹配规则
- category 是一个字符串,系统预定义了一些 category,同时我们也可以在应用中定义自己的 category
- Intent 中如果含有 category,那么所有的 category 都必须和过滤规则中的其中一个 category 相同
- 即,Intent 中如果出现了 category,不管有几个 category,对于每个 category 来说,它必须是过滤规则中已经定义了的 category
- Intent 中不调用
addcategory()
方法设置 category 也是可以的原因- 系统在调用
startActivity()
或者startActivityForResult()
方法的时候会默认为 Intent 加上"android.intent.category.DEFAULT"
这个 category - 为了目标 Activity 能够接受隐式调用,就必须在 intent-filter 中指定
"android.intent.category.DEFAULT"
这个 category
- 系统在调用
4. data 的匹配规则
和 action 类似,如果过滤规则中定义了 data,那么 Intent 中必须也要定义可匹配的 data
data 的结构
data 的语法:
1
2
3
4
5
6
7<data android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string" />data 由两部分组成,mimeType 和 URI
- mimeType 指媒体类型,比如 image/jpeg、audio/mpeg4-generic 和 video/* 等,可以表示图片、文本、视频等不同的媒体格式
- URI 是统一资源标识符,格式为:
:// : /[ | | ]
URI 中每个数据的含义
- Scheme: URI 的模式,比如 http、file、content 等,如果 URI 中没有指定 scheme,那么整个 URI 的其他参数无效,这也意味着 URI 是无效的
- Host: URI 的主机名,比如 www.google.com,如果 host 未指定,那么整个 URI 中的其他参数无效,这也意味着 URI 是无效的
- Port: URI 中的端口号,比如 80,仅当 URI 中指定了 scheme 和 host 参数的时候 port 参数才是有意义的
- Path/pathPattern/pathPrefix: 这三个参数表述路径信息,其中 path 表示完整的路径信息;pathPattern 也表示完整的路径信息,但是它里面可以包含通配符
*
,通配符*
表示 0 个或多个任意字符。需要注意的是,由于正则表达式的规范,如果想表示真实的字符串,那么*
要写成\\*
、\
要写成\\\\
;pathPrefix 表示路径的前缀信息
5. data 的匹配示例
demo 1:
1
2
3
4<intent-filter>
<data android:mimeType="image/*" />
...
</intent-filter>- 这种规则制定了媒体类型为所有类型的图片,那么 Intent 中的 mimeType 属性必须为 image/* 才能匹配
- 这种情况下虽然过滤规则没有指定 URI,但是却有默认值,URI 的默认值为 content 和 file
- 也就是说,虽然没有指定 URI,但是 Intent 中的 URI 部分的 schame 必须为 content 或者 file 才能匹配。如果是 http,则会报错:
android.conten.ActivityNotFoundException: No Activity found to handle Intent {...}
- 如果要为 Intent 指定完整的 data,必须要调用
setDataAndType()
方法,不能先调用setData()
再调用setType()
,因为从这两个方法的源码看这两个方法彼此会清除对方的值赋值为null
- 匹配示例:
intent.setDataAndType(Uri.parse("file://abc"), "image/png");
demo 2:
1
2
3
4
5<intent-filter>
<data android:mimeType="video/mpeg" android:scheme="http" ... />
<data android:mimeType="audio/mpeg" android:scheme="http" ... />
...
</intent-filter>- 匹配示例 1:
intent.setDataAndType(Uri.parse("http://abc"), "video/mpeg");
- 匹配示例 2:
intent.setDataAndType(Uri.parse("http://abc"), "audio/mpeg");
- 匹配示例 1:
demo 3: 下面两种写法,二者的作用是一样的
1
2
3
4<intent-filter>
<data android:scheme="file" android:host="www.google.com" />
...
</intent-filter>1
2
3
4
5<intent-filter>
<data android:scheme="file" />
<data android:host="www.google.com" />
...
</intent-filter>
6. data 匹配的注意事项
intent-filter 的匹配规则对于 Service 和 BroadcastReceiver 也是同样的道理。但是,系统对于 Service 的建议是尽量使用显示调用方式来启动服务
当我们通过隐式方式启动一个 Activity 的时候,可以做一下判断,看是否有 Activity 能够匹配我们的隐式 Intent,如果不做判断就有可能出现上述 demo1 的错误了。判断方法有两种:
- 采用 PackageManager 的
resolveActivity()
方法或者 Intent 的resolveActivity()
方法,如果找不到匹配的 Activity 就会返回 null,可以通过判断是否为 null 来规避上述报错 - PackageManager 还提供了
queryIntentActivity()
方法,这个方法和resolveActivity()
方法不同的是:它不是返回最佳匹配的 Activity 信息而是返回所有成功匹配的 Activity 信息
- 采用 PackageManager 的
在 action 和 category 中,有一类 action 和 category 比较重要,它们是:
1
2<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER />- 这二者共同作用是用来标明这是一个入口 Activity 并且会出现在系统的应用列表中,少了任何一个都没有实际意义,也无法出现在系统的应用列表中,二者缺一不可
针对 Service 和 BroadcastReceiver,PackageManager 同样提供了类似的方法去获取成功匹配的组件信息