1.IntentFilter的匹配规则
我们知道,启动Activity
分为两种,显式调用和隐式调用。两者的区别这里就不多说了,显式调用需要明确地指定被启动对象的组件信息,包括包名和类名,而隐式调用则不需要明确指定组件信息。原则上一个Intent
不应该即是显式调用又是隐式调用,如果两者共存的话,以显式调用为主。显式调用很简单,这里主要介绍一下隐式调用。隐式调用需要Intent
能够匹配目标组件的IntentFilter
中所设置的过滤信息,如果不匹配将无法启动目标Activity
。IntentFilter
中的过滤信息有action
,category
,data
。
为了匹配过滤列表,需要同时匹配过滤列表中的action
,category
,data
信息,否则匹配失败。一个过滤列表中的action
,category
和data
可以有多个,所有的action
,category
,data
分别构成不同类别,同一类别的信息共同约束当前类别的匹配过程。只有一个Intent
同时匹配action
类别,category
类别,data
类别才算完全匹配,只有完全匹配才能成功启动目标Activity
。另外一点,一个Activity
中可以有多个intent-filter
,一个Intent
只要能匹配任何一组intent-filter
即可成功启动对应的Activity
。
1. action的匹配规则
action
是一个字符串,系统预定义了一些action
,同时我们也可以在应用中定义自己的action
,action
的匹配规则是Intent
中的action
必须能够和过滤规则中的action
匹配,这里说的匹配是指action
的字符串值完全一样。一个过滤规则中可以有多个action,那么只要Intent
中的action
能够和过滤规则中的任何一个action
相同即可匹配成功。需要注意的是,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
也可以匹配呢?原因是系统在调用startActivity
或者startActivityForResult
的时候会默认为Intent
加上这个category
,所以这个category
就可以匹配前面的过滤规则中的第三个category
。同时,为了我们的Activity
能够接收隐式调用,就必须在intent-filter
中指定这个category
,原因刚才已经说明了。
3. data的匹配规则
data
的匹配规则和action
类似,如果过滤规则中定义了data
,那么Intent
必须也要定义可匹配的data
。在介绍data
的匹配规则之前,我们需要先了解一下data
的结构,因为data
稍微有些复杂。
data的语法如下所示:
data
由两部分组成,mimeType
和URI
。mimeType
指媒体类型,比如image/jpeg
,audio/mpeg4-generic
和video/*
等,可以表示图片,文本,视频等不同的媒体格式,而URI
中包含的数据就比较多了。
下面是URI
的结构:
这里再给几个实际的例子就比较好理解了,如下所示:
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、pathPattern和pathPrefix:这三个参数表述路径信息,其中path
表示完整的路径信息,pathPattern
也表示完整的路径信息,但是它里面可以包含通配符*
,*
表示0个或多个任意字符,需要注意的是,由于正则表达式的规范,如果想表示真实的字符串,那么*
要写成\\*
,\
要写成\\\\
,pathPrefix表示路径的前缀信息。
介绍完data
的数据格式后,我们要说一下data
的匹配规则了,data
的匹配规则和action
类似,它也要求Intent
中必须含有data
数据,并且data
数据能够完全匹配过滤规则中的某一个data
,这里的完全匹配是指过滤规则中出现的data
部分也出现在了Intent
中的data
中。
···
这种规则指定了媒体类型为所有类型的图片,那么Intent
中的mimeType
属性必须为image/*
才能匹配,这种情况下虽然过滤规则没有指定URI
,但是却有默认值,URI
的默认值为content
和file
。也就是说,虽然没有指定URI
,但是Intent
中的URI
部分的scheme
必须为content
或者file
才能匹配,这点是需要尤其注意的。
为了匹配(1)中的规则,可以写出如下实例:
intent().setDataAndType(Uri.parse("file://abc"),"image/png");
另外,如果要为Intent
指定完整的data
,必须要调用setDataAndType
方法,不能先调用setData
再调用setType
,因为这两个方法彼此会清除对方的值。
public Intent setData(@Nullable Uri data) {
mData = data;
mType = null;
return this;
}
可以发现,setData
会把mimeType
置为null
,同理setType
也会把URI
置为null
。
(2)如下过滤规则
...
这种规则指定了两组data
规则,且每个data
都指定了完整的属性值,既有URI
又有mimeType
。为了匹配(2)中规则,我们可以写出如下实例:
Intent().setDataAndType(Uri.parse("http://abc"),"video/mpeg");
或
Intent().setDataAndType(Uri.parse("http://abc"),"audio/mpeg");
通过上面两个实例,应该已经明白了data
的匹配规则,关于data
还有一个特殊情况需要说明下,这也是它和action
不同的地方,如下两种特殊的写法,他们的作用是一样的。
...
和
...
intent-filter
的匹配规则对于Service
和BroadcastReceiver
也是同样的道理,不过系统对于Service
的建议是尽量使用显式调用方式来启动服务。
最后,当我们通过隐式方式启动一个Activity
的时候,可以做一下判断,看是否有Activity
能够匹配我们的隐式Intent
,如果不做判断就有可能出现上述的错误了。判断方法有两种:采用PackageManager
的resolveActivity
方法或者Intent
的resolveActivity
方法,如果它们找不到匹配的Activity
就会返回null
,我们通过判断返回值就可以规避上述错误了。另外,PackageManager
还提供了queryIntentActivities
方法,这个方法和resolveActivity
方法不同的是:它不是返回最佳匹配的Activity信息而是返回所有成功匹配的Activity信息。
if (getPackageManager().resolveActivity(intent,PackageManager.MATCH_DEFAULT_ONLY) != null)
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(chooser);
}
我们看一下queryIntentActivities
和resolveActivity
的方法原型:
public abstract List queryIntentActivities(Intent intent,
@ResolveInfoFlags int flags);
public abstract ResolveInfo resolveActivity(Intent intent,
@ResolveInfoFlags int flags);
上述两个方法的第一个参数比较好理解,第二个参数需要注意,我们要使用PackageManager.MATCH_DEFAULT_ONLY
这个标记位,这个标记位的含义是仅仅匹配那些在intent-filter
中声明了这个
的Activity
。使用这个标记位的意义在于,只要上述两个方法不返回null
,那么startActivity
一定可以成功。如果不用这个标记位,就可以把intent-filter
中category
不含DEFAULT
的那些Activity
给匹配出来,从而导致startActivity
可能失败。因为不含有DEFAULT
这个category
的Activity
是无法接收隐式Intent
的。
在action
和category
中,有一类action
和category
比较重要,它们是:
这两者共同作用是用来标明这是一个入口Activity
并且会出现在系统的应用列表中,少了任何一个都没有实际意义,也无法出现在系统的应用列表中,也就是二者缺一不可。