Android开发艺术探索知识回顾——第1章 Activity的生命周期和启动模式:3、IntentFilter的匹配规则 action、category、data

 

1.3 IntentFilter的匹配规则

我们知道,启动 Activity 分为两种,显式调用和隐式调用。二者的区别这里就不多说,显式调用需要明确地指定被启动对象的组件信息,包括包名和类名,而隐式调用则不需要明确指定组件信息。原则上一个 Intent 不应该既是显式调用又是隐式调用,如果二者共存的话以显式调用为主

显式调用很简单,这里主要介绍一下隐式调用。隐式调用需要 Intent 能够匹配目标组件的 IntentFilter 中所设置的过滤信息,如果不匹配将无法启动目标 Activity。IntentFilter 中的过滤信息有 action、category、data,下面是一个过滤规则的示例:

        <activity
            android:name="com.yyh.demo6.IntentFilter.ThirdActivity"
            android:configChanges="screenLayout"
            android:label="@string/app_name"
            android:taskAffinity="com.ryg.task1" 
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="com.ryg.charpter_1.c" />
                <action android:name="com.ryg.charpter_1.d" />
                <category android:name="com.ryg.category.c" />
                <category android:name="com.ryg.category.d" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="text/plain" />
         	</intent-filter>
         </activity>

为了匹配过滤列表,需要同时匹配过滤列表中的 action、category、data 信息,否则匹配失败。一个过滤列表中的 action、category 和data 可以有多个,所有的 action、category、 data 分别构成不同类别,同一类别的信息共同约束当前类别的匹配过程。

只有一个 Intent 同时匹配action类别、category类别、data类别才算完全匹配,只有完全匹配才能成功启动目标 Activity。另外一点,一个 Activity 中可以有多个 intent-filter,一个 Intent 只要能匹配任何一组 intent-filter 即可成功启动对应的 Activity,如下所示。

         <activity android:name="ShareActivity">
            <!-- This activity handlers "SEND" actions with text data-->
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="text/plain" />
            </intent-filter>
            <!--This activity also handlers "SEND" and "SEND_MULTIPLE" with media data-->
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <action android:name="android.intent.action.SEND_MULTIPLE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="application/vnd.google.panorama360+jpg"/>
                <data android:mimeType="image/*" />
                <data android:mimeType="video/*" />
            </intent-filter>
        </activity>

 

下面详细分析各种属性的匹配规则。

1. action的匹配规则

action 是一个字符串,系统预定义了一些 action,同时我们也可以在应用中定义自己的 action。action 的匹配规则是 Intent 中的 action 必须能够和过滤规则中的 action 匹配,这里说的匹配是指 action 的字符串值完全一样。

一个过滤规则中可以有多个 action,那么只要 Intent 中的 action 能够和过滤规则中的任何一个 action 相同即可匹配成功。针对上面的过滤规则,只要我们的 Intent 中 action 值为 “com.ryg.charpter_l.c” 或者 “com.ryg.charpter_ l.d” 都能成功匹配。需要注意的是,Intent中如果没有指定 action,那么匹配失败。

总结一下,action的匹配要求 Intent 中的 action 存在且必须和过滤规则中的其中一个 action 相同,这里需要注意它和 category 匹配规则的不同。另外,action 区分大小写,大小写不同字符串相同的 action 会匹配失败。

 

2. category的匹配规则

category 是一个字符串,系统预定义了一些 category,同时我们也可以在应用中定义自己的 category。category 的匹配规则和 action 不同,它要求 Intent 中如果含有 category,那么所有的 category 都必须和过滤规则中的其中一个 category 相同。换句话说,Intent 中如果出现了 category,不管有几个category,对于每个category来说,它必须是过滤规则中已经定义了的 category。

当然,Intent中可以没有 category,如果没有 category 的话,按照上面的描述,这个 Intent 仍然可以匹配成功。这里要注意下它和 action 匹配过程的不同,action 是要求 Intent 中必须有一个 action 且必须能够和过滤规则中的某个 action 相同,而 category 要求 Intent 可以没有category,但是如果你一旦有 category,不管有几个,每个都要能够和过滤规则中的任何一个 category 相同。

为了匹配前面的过滤规则中的 category,我们可以写出下面的 Intent,intent.addcategory ("com.ryg.category.c") 或者 Intent.addcategory ("com.ryg. category.d")  亦或者不设置 category。

为什么不设置 category 也可以匹配呢?

原因是系统在调用 startActivity 或者 startActivityForResult 的时候会默认为 Intent 加上 " android.intent.category.DEFAULT " 这个 category,所以这个 category 就可以匹配前面的过滤规则中的第三个 category。同时,为了我们的activity能够接收隐式调用,就必须在 intent-filter 中指定 "android.intent.category.DEFAULT" 这个 category,原因刚才已经说明了。

 

3. data的匹配规则

data 的匹配规则和 action 类似,如果过滤规则中定义了 data,那么 Intent 中必须也要定义可匹配的 data。在介绍 data 的匹配规则之前,我们需要先了解一下 data 的结构,因为 data 稍微有些复杂。

data的语法如下所示:

    <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>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]

这里再给几个实际的例子就比较好理解了,如下所示。
 

content://com.example.project:200/folder/subfolder/etc 

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中的端口号,比如80,仅当 URI 中指定了 scheme 和 host 参数的时候 port 参数才是有意义的。

Path、pathPattem 和 pathPrefix:这三个参数表述路径信息,其中 path 表示完整的路径信息;pathPattem 也表示完整的路径信息,但是它里面可以包含通配符 "*","*" 表示 0 个或多个任意字符,需要注意的是,由于正则表达式的规范,如果想表示真实的字符串,那么"*"要写成 "\\*","\"要写成"\\\\";pathPrefix表示路径的前缀信息。

介绍完 data 的数据格式后,我们要说一下 data 的匹配规则了。前面说到,data 的匹配规则和 action 类似,它也要求 Intent 中必须含有 data 数据,并且 data 数据能够完全匹配过滤规则中的某一个 data。这里的完全匹配是指过滤规则中出现的 data 部分也出现在了 Intent 中的 data 中。下面分情况说明。

(1)如下过滤规则:

       <intent-filter>
          <data android:mimeType="image/*"/>
      </intent-filter>

这种规则指定了媒体类型为所有类型的图片,那么 Intent 中的 mimeType 属性必须为 "image/*" 才能匹配,这种情况下虽然过滤规则没有指定 URI,但是却有默认值,URI 的默认值为 content 和 file 。

也就是说,虽然没有指定 URI,但是 Intent 中的 URI 部分的 schema 必须为 content 或者 file 才能匹配,这点是需要尤其注意的。为了匹配 (1) 中规则,我们可以写出如下示例:

    intent.setDataAndType(Uri.parse("file://abc"),"image/png");

另外,如果要为 Intent 指定完整的 data,必须要调用 setDataAndType 方法,不能先调用 setData 再调用 setType,因为这两个方法彼此会清除对方的值,这个看源码就很容易理解,比如setData:

    public Intent setData(Uri data) {
        mData = data;
        mType = null;
        return this;
    }

可以发现,setData 会把 mimeType 置为null,同理 sctType 也会把 URI 置为 null。

 

(2)如下过滤规则:

      <intent-filter>
           <data android:mimeType="video/mpeg" android:scheme="http" .../>
           <data android:mimeType="audio/mpeg" android:scheme="http" .../>
      </intent-filter>

这种规则指定了两组 data 规则,且每个 data 都指定了完整的属性值,既有 URI 又有 mimeType。为了匹配 (2) 中规则,我们可以写岀如下示例:

    intent.setDataAndType(Uri.parse("http://abc"),"video/png");

或者

    intent.setDataAndType(Uri.parse("http://abc"),"audio/png"); 

通过上面两个示例,读者应该已经明白了 data 的匹配规则,关于 data 还有一个特殊情况需要说明下,这也是它和 action 不同的地方,如下两种特殊的写法,它们的作用是一样的:

  <intent-filter >
     <data android:scheme="file" android:host="www.baidu.com"/>
         ...
 </intent-filter>


 <intent-filter >
       <data android:scheme="file" />
       <data android:host="www.baidu.com"/>
         ...
  </intent-filter>

到这里我们已经把 IntentFilter 的过滤规则都讲解了一遍,还记得本节前面给出的一个 intent-filter 的示例吗?现在我们给出完全匹配它的Intent:

    Intent intent = new Intent("com.ryg.charpter_1.c");
    intent.addCategory("com.ryg.category.c");
    intent.setDataAndType(Uri.parse("file//abc"),"text/plain");
    startActivity(intent);

还记得 URI 的 schema 是有默认值的吗?

如果把上面的 intent.setDataAndType(Uri.parse("file://abc"), text/plain") 这句成 intent.setDataAndType(Uri.parse("http://abc"), "text/plain")打开 Activity 的时候就会报错,提示无法找到 Activity,如图1-10所示。

另外 一点,Intent-filter的匹配规则对于Service和BroadcastReceiver 也是同样的道理,不过系统对于 Service 的建议是尽量使用显式调用方式来启动服务。

 

最后,当我们通过隐式方式启动一个 Activity 的时候,可以做一下判断,看是否有 Activity 能够匹配我们的隐式 Intent,如果不做判断就有可能出现上述的错误了。判断方法有两种:釆用 PackageManager 的 resolveActivity 方法或者 Intent 的 resoIveActivity 方法,如果它们找不到匹配的 Activity 就会返回 null,我们通过判断返回值就可以规避上述错误了。

另外,PackageManager 还提供了 querylntentActivities 方法,这个方法和 resolveActivity 方法不同的是:它不是返回最佳匹配的 Activity信息而是返回所有成功匹配的 Activity 信息。 我们看一下 querylntentActivities 和 resolveActivity 的方法原型:

    public abstract List<ResolveInfo>queryIntentActivities(Intent intent,int fladgs);
    public abstract ResolveInfo resolveActivity(Intent intent,int flags);

上述两个方法的第一个参数比较好理解,第二个参数需要注意,我们要使用 MATCH_ DEFAULT_ONLY 这个标记位,这个标记位的含义是仅仅匹配那些在 intent-filter 中声明了 <category android:name="android.intent.category.DEFAULT"/> 这个 category 的 Activity。

使用这个标记位的意义在于,只要上述两个方法不返回null,那么 startActivity 一定可以成功。 如果不用这个标记位,就可以把intent-filter中 category 不含 DEFAULT 的那些 Activity 给匹配出来,从而导致 startActivity 可能失败。因为不含有 DEFAULT 这个 category 的 Activity 是无法接收隐式 Intent 的。在 action 和 category 中,有一类 action 和 category 比较重要,它们是:

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

这二者共同作用是用来标明这是一个入口 Activity 并且会出现在系统的应用列表中,少了任何一个都没有实际意义,也无法出现在系统的应用列表中,也就是二者缺一不可。 另外,针对 Service 和 BroadcastReceiver,PackageManager 同样提供了类似的方法去获取成功匹配的组件信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

被开发耽误的大厨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值