0%

Activity 的生命周期和启动模式(三):IntentFilter 的匹配规则

1. IntentFilter 的匹配规则

  • 启动 Activity 分为两种:显式调用隐式调用,分别对应显式 Intent隐式 Intent,具体来说,分别对应 Intent 的不同的重载方法

    • 隐式调用需要 Intent 能够匹配目标 Activity 的 intent-filter 标签中所设置的过滤信息,如果不匹配将不能启动目标 Activity
    • IntentFilter 中的过滤信息有 actioncategorydata,这三个需要同时匹配才能启动成功
  • 一个 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 由两部分组成,mimeTypeURI

      • mimeType 指媒体类型,比如 image/jpeg、audio/mpeg4-generic 和 video/* 等,可以表示图片、文本、视频等不同的媒体格式
      • URI 是统一资源标识符,格式为:<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
    • 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");
  • 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 的匹配规则对于 ServiceBroadcastReceiver 也是同样的道理。但是,系统对于 Service 的建议是尽量使用显示调用方式来启动服务

  • 当我们通过隐式方式启动一个 Activity 的时候,可以做一下判断,看是否有 Activity 能够匹配我们的隐式 Intent,如果不做判断就有可能出现上述 demo1 的错误了。判断方法有两种:

    • 采用 PackageManager 的 resolveActivity() 方法或者 Intent 的 resolveActivity() 方法,如果找不到匹配的 Activity 就会返回 null,可以通过判断是否为 null 来规避上述报错
    • PackageManager 还提供了 queryIntentActivity() 方法,这个方法和 resolveActivity() 方法不同的是:它不是返回最佳匹配的 Activity 信息而是返回所有成功匹配的 Activity 信息
  • 在 action 和 category 中,有一类 action 和 category 比较重要,它们是:

    1
    2
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER />
    • 这二者共同作用是用来标明这是一个入口 Activity 并且会出现在系统的应用列表中,少了任何一个都没有实际意义,也无法出现在系统的应用列表中,二者缺一不可
  • 针对 Service 和 BroadcastReceiver,PackageManager 同样提供了类似的方法去获取成功匹配的组件信息

-------------------- 本文结束感谢您的阅读 --------------------