聊聊Android中intent-filter匹配规则

  • 内容
  • 评论
  • 相关

我们知道Activity的启动方式分为隐式和显式两种,至于两者的区别想必大家都已经很清楚了,显式需要明确的指定被启动对象的组件信息,比如包名和类名等,而隐式调用就不需要明确指定组件的信息。隐式调用就需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息,如果匹配不成功就不能启动目标Activity。

IntentFiler中过滤的信息包含action,category,data,如下边示例所示,本文也将围绕这个示例进行讲解。

<activity android:name="com.aoaoyi.intentfilter.MyActivity">
    <intent-filter>
        <action android:name="com.aoaoyi.a"/>
        <action android:name="com.aoaoyi.b"/>

        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="com.aoaoyi.c"/>
        <category android:name="com.aoaoyi.d"/>

        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

总的原则是:

  1. 匹配过滤列表时需要同时匹配过滤列表中的action,category,data;
  2. 一个过滤列表中可以有多个action,category,data并各自构成不同类别,一个Intent必须同时匹配action类别,category类别和data类别才算完全匹配;
  3. 一个Activity中可以有多组intent-filter,一个Intent只要匹配任何一组intent-filter就算匹配成功;

下边将分别介绍3种属性的匹配规则

1.action的匹配规则

我们知道action是一个字符串,系统默认帮我们定义了一些action,同时我们也可以自定义。字符串中的字幕是严格区分大小写的,这个在匹配的时候是要注意的。关于action的匹配很简单,我们可以直接总结归纳一下:

Intent中必须存在action,这一点和category不同;

action的字符串严格区分大小写,intent中的action必须和过滤规则中的action完全一致才能匹配成功;

匹配规则中可以同时有多个action,但是Intent中的action只需与其中之一相同即可匹配成功;

那么按照以上总结的规则,为了匹配本文开头的示例,那么Intent的action可以为“com.aoaoyi.a”或者是“com.aoaoyi.a。

2.category的匹配规则

和action一样,category也是一个字符串,系统同样默认定义了一些,我们一样可以自定义。其实category的匹配规则和action有很强的可比性,所以我们可以完全类比action来进行总结

匹配规则中必须添加“android.intent.category.DEFAULT”这个过滤条件;

Intent中可以不设置category,这个时候你在使用startActivity或者startActivityForResult的时候,其实系统自动会为你添加1中的那个默认category;

Intent中可以同时设置多个category,一旦设置多个catrgory,那么每个category都必须能够和过滤条件中的某个category匹配成功.

我们发现3中的规则和action的匹配规则有所不同,action有多个的时候,只要其中之一能够匹配成功即可,但是category必须是每一个都需要匹配成功,这个对比起来还是挺好记住的吧。那么按照这个规则的话,为了匹配示例中的过滤条件,我们可以这样设置category:intent.addCategory(“com.aoaoyi.c”);或者是intent.addCategory(“com.aoaoyi.d”);在或者是不设置category

3.data的匹配规则

在介绍data的匹配之前,我觉得还是有必要再温故一下data的结构吧

<data android:scheme=“string”

      android:host=“string”

      android:port=“80”

      android:path=“/string”

      android:pathPattern=“string”

      android:pathPrefix=“/string”

      android:mimeType=“text/plain”/>

总的来说data包含两部分:mimeType和URI。

  • mimeType表示image/ipeg,video/*等媒体类型
  • URI信息量相对大一点,其结构一般为:<scheme>://<host>:<port>/[<path>|<pathPrefix|<pathPattern>>]

下边讲分别来介绍下各个节点数据的含义

  1. scheme:整个URI的模式,如常见的http,file等,注意如果URI中没有指定的scheme,那么整个uri无效
  2. host:URI的域名,比如我们常见的www.mi.com,www.baidu.com,与scheme一样,一旦没有host那么整个URI也毫无意义;
  3. port:端口号,比如80,很容易理解,只有在URI中指定了scheme和host之后端口号才是有意义的;
  4. path,pathPattern,pathPrefix包含路径信息,path表示完整的路径,pathPattern在此基础上可以包含通配符,pathPrefix表示路径的前缀信息;

OH,data的格式介绍完毕。

其实data的匹配规则和action也有点类似:

Intent中必须有data数据

Intent中的data必须和过滤规则中的某一个data完全匹配

过滤规则中可以有多个data存在,但是Intent中的data只需匹配其中的任意一个data即可

过滤规则中可以没有指定URI,但是系统会赋予其默认值:content和file,这一点在Intent中需要注意

为Intent设定data的时候必须要调用setDataAndType()方法,而不能先setData再setType,因为这两个方法是互斥的,都会清除对方的值,这个有兴趣可以参见源码

在匹配规则中,data的scheme,host,port,path等属性可以写在同一个< />中,也可以分开单独写,其功效是一样的

OK,说到这里,action,category和data的匹配规则基本都说完了,我们现在可以完全匹配一下文章开头的过滤条件示例了:

 Intent _Intent = new Intent("com.aoaoyi.a");
 _Intent.addCategory("com.aoaoyi.c");
 _Intent.setDataAndType(Uri.parse("file://abc"), "text/plain");
 startActivity(intent);
4.匹配失败

前边我们说到data的URI默认是content和file,所以如果你把_Intent.setDataAndType(Uri.parse(“file://abc”), “text/plain”);中的URI “file://abc”改为”http://abc”的话,就会出现以下异常

1986815-f9fd968f175453041

5.杜绝异常的发生

为了避免发生以上异常,我们可以使用PackageManager或者Intent的resolveActivity()方法,,如果intent和过滤规则匹配失败,那么将返回null,我们也就不会再继续调用startActivity方法啦,从而去修改intent再次进行匹配直到成功匹配到预期intent。

6.延伸

前文中也提到了在action和category中有一些是系统自带的,其中有些比较重要,比如

<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/>

这两者一般成双成对出现,用来表明这是一个程序的入口,并且会出现在系统的应用列表中。之所以成双成对的出现是说,二者缺一不可,少了彼此都没有实际意义。

另外本文讲到的所有intent-filter匹配规则同样适用于Service和BroadcastReceiver,但是在Android5.0以后需要显式调用来启动Service,否则会报异常:java.lang.IllegalArgumentException: Service Intent must be explicit。