IntentFilter的匹配规则
安卓使用隐式调用时,需要Intent能够匹配目标组件的IntentFilter中设置的过滤信息,如果不匹配将无法启动目标Activity,IntentFilter中的过滤信息有action、category、data。
- 一个Activity中可以有多个intent-filter,只要intent能够匹配中其中一组就能够启动Activity
- 只有一个Intent能够同时匹配action类别,category类别,data类别才能算完全匹配
1.action的匹配规则
action是一个字符串(区分大小写),acion的匹配规则是Intent中的action必须能够和过滤规则中的action匹配(字符串内容完全相等),一个过滤规则中可以有多个action,只要intent中的action能够和过滤规则中的任何一个action相同就能匹配成功。如果匹配失败,抛出异常android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.custom }
- 隐式启动
<activity
android:name=".MainActivity2"
android:exported="false">
<intent-filter>
<!--这里有两个action-->
<action android:name="android.intent.action.custom"/>
<action android:name="android.intent.action.west"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
findViewById<TextView>(R.id.tvHello).setOnClickListener {
Intent().also {
it.action = "android.intent.action.custom"
startActivity(it)
}
}
结果:启动成功,证明action只需要匹配其中一个即可。
2.category的匹配规则
cagegory是一个字符串,它的匹配规则是,如果一个intent中含有category,那么AndroidManifest.xml中定义的所有的category都必须和过滤规则中的每一个category相同。
为什么上面的例子中,不设置category也能够匹配成功呢?因为系统在调用startActivity时会默认加上andoid.intent.category.DEFAULT,所以在AndroifManifest.xml中也需要加上andoid.intent.category.DEFAULT,否则也无法匹配。
3.data的匹配规则
与action类似,能命中intent-filter中data的其中一组,就可以打开activity
data的语法结构:
<data android:scheme="string"
android:host="string"
android:port="80"
android:path="/string"
android:pathPattern="string"
android:pathPrefix="/string"
android:mimeType="string"/>
data由两部分组成:miniType和URI,miniType指媒体类型,比如image/jpeg、video/*等
URI结构:😕/:/[
URI例子:http://www.baidu.com:80/search/info
- Scheme:URI的模式,比如http、file、content等,如果URI中没有指定scheme,那么整个URI的其他参数将无效,意味着URI也无效。
- Host:URI的主机名,比如www.baidu.com,如果host未指定,那么整个URI中的其他参数无效,意味着URI也是无效的。
- Port:URI的端口号,仅当URI中指定了scheme和host参数的时候,port才是有意义的。
- Path、pathPattern和pathPrefix:这三个参数表述路径的信息,其中path表示完整的路径信息,pathPattern也表示完整的路径信息,但是它里面可以包含通配符“”,“”表示0个或多个任意字符(由于正则表达式的规范,如果想表示真实的字符串,那么“”要写成“\”,“\”要写成"\\",pathPattern表示路径的前缀信息)
- data匹配例子
<intent-filter>
<action android:name="android.intent.action.west"/>
<category android:name="android.intent.category.DEFAULT"/>
<data
android:scheme="west"
android:host="com.example.west"
android:mimeType="image/jpeg"/>
Intent().also {
it.action = "android.intent.action.west"
it.setDataAndType(Uri.parse("west://com.example.west"), "image/*")
startActivity(it)
}
该intent能够启动对应的activity,mimeType=“image/*”,表示启动所有图片类型的activity,intent-filter指定data的mimeType为“image/jpeg”表示我的activity可以处理jpeg类型的图片,所以可以启动,URI中的scheme和host也是匹配的。
《Android开发艺术探索》中提到:“scheme”是有默认值的,为"file"或“content”,也就是说,我们在AndroidManifest中写data时,可以不写scheme,它的默认值就是file或content,通过intent匹配时,scheme要指定为file或者content就可以匹配(file亲测不行,content可以)。
- 如果没有指定scheme,其他参数都是无效的,举例
<activity
android:name=".MainActivity2"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.west"/>
<category android:name="android.intent.category.DEFAULT"/>
<data
<!--不指定scheme,默认用content可以启动-->
android:host="com.example.west"
android:mimeType="image/jpeg"/>
</intent-filter>
</activity>
it.action = "android.intent.action.west"
it.setDataAndType(Uri.parse("content://com.exfample.west"), "image/*")
val resolveActivity = packageManager
.resolveActivity(it, PackageManager.MATCH_DEFAULT_ONLY)
startActivity(it)
这里的host和androidManifest中对不上,但是隐式启动activity成功,说明我们没在androidManifest中指定scheme,其他参数都是无效的,这里URI的scheme需要写成content,原因上面有提到。
使用隐式启动时,如何避免报错
使用PackageManage的resolveActivity
it.action = "android.intent.action.west"
it.setDataAndType(Uri.parse("content://com.exampleeee.west"), "image/*")
val resolveActivity = packageManager.resolveActivity(it, PackageManager.MATCH_DEFAULT_ONLY)
如果我们的intent-filter规则能够匹配某个activity,resolveActivity返回对应的Activity,否则为null,我们在使用隐式启动前通过resolveActivity判断有没有可以启动的目标Activity即可。
参考资料:《Android开发艺术探索》