1:概述
我们知道启动Activity分为两种,显式调用和隐式调用。隐式调用需要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。下面详细分析各种属性的匹配规则。
2、action的匹配规则
action是一个字符串,系统预定义了一些action,同时我们也可以在应用中定义自己的action。action的匹配是Intent中的action必须能够和过滤规则中的action匹配,这里说的匹配是指action的字符串值完全一样。一个过滤规则中可以有多个action,那么只要Intent中的action能够和过滤规则中的任何一个action相同即可匹配成功(一个或者多个都行),action区分大小写,大小写不同会导致匹配失败。也就是说,如果intent-filter中如果存在action,那么Intent必须有action且不需和过滤规则中的其中一个action相同。
3、category的匹配原则
category是一个字符串,。category的匹配规则和action不同,它要求Intent中如果含有category,那么所有的category都必须和过滤规则中的其中给一个category相同,与action不同的是,intent中可以没用category,如果没有的话也可以匹配成功,同时,为了我们的activity能够接受隐式调用,就必须在intent-filter中添加“android.intent.category.DEFAULT”这个category。
Action测试
为了指定能够接收并处理的Intent的类型,组件可以在intent-filter中声明其支持0个或多个action,例如:
<intent-filter>
<action android:name="com.ispring.action.ACTION_TEST1" />
<action android:name="com.ispring.action.ACTION_TEST2" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
intent对象可以通过setAction()方法设置唯一的一个action值。对于action测试,需要分两种情况:
intent对象设置了action
如果intent对象通过调用setAction()方法设置了action的值,那么只有当组件的intent-filter中包含了intent对象中的action值的时候,action测试才通过,否则无法通过。
举个例子,假设我们的Activity的intent-filter如下所示:<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <action android:name="com.ispring.action.ACTION_TEST2" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> </intent-filter>
下面的intent对象可以通过上面intent-filter里面的action测试:
Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1"); Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun"); intent.setData(uri);
该intent之所以能通过action测试是因为intent-filter中包含该intent的action值com.ispring.action.ACTION_TEST1。
下面的intent对象无法通过上面intent-filter里面的action测试:
Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST3"); Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun"); intent.setData(uri);
该intent之所以无法通过action测试是因为intent-filter中不包含该intent的action值com.ispring.action.ACTION_TEST3。
intent对象没有设置action
如果intent对象没有调用setAction()方法设置action的值,那么如果intent-filter至少有一个任意的action的值,该intent对象就可以通过该intent-filter的action测试,反之,如果intent-filter中没有定义任何的action,那么该intent无法通过该intent-filter的action测试。
举个例子,假设我们的intent对象如下所示:Intent intent = new Intent(); //不设置action值 //intent.setAction("com.ispring.action.ACTION_TEST1"); Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun"); intent.setData(uri);
上面的intent对象可以通过如下的intent-filter:
<intent-filter> <action android:name="com.csdn.action.ACTION_XXX" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> </intent-filter>
上面的intent对象无法通过如下的intent-filter:
<intent-filter> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> </intent-filter>
通过上面的几个示例,想必大家都已经理解了action测试的规则,至于上面的category和data标签的使用,会在下面详细介绍。
总结起来有两点结论:
1. 要想让intent对象通过action测试,那么intent-filter中声明的action不能为空且要包含intent对象中的action值(如果intent的action值不为空的话)。
2. 如果intent-filter没有声明任何action,那么所有的intent的对象(即无论intent如何配置)都无法通过intent-filter的action测试。
Category测试
为了指定能够接收并处理的Intent的类型,组件可以在intent-filter中声明其支持0个或多个category,例如:
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
...
</intent-filter>
intent对象有addCategory()方法,也就是说一个intent对象也可以关联多个category。为了能让intent对象通过intent-filter的category测试,intent对象中的所有category都要在intent-filter中找到对应项。
具体来说,又分为如下两种情况:
intent对象至少有一个category
这种情况下,假设intent对象有N个category(N >=1),那么intent-filter中必须要包含这N个category,intent对象才能通过category测试,否则无法通过测试。如果用intent对象启动Activity,还有其他限制条件,会在后面详细说明。
举个例子,假设我们的intent-filter如下所示:<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="com.ispring.category.TEST1" /> <category android:name="com.ispring.category.TEST2" /> </intent-filter>
以下intent对象能够通过category测试
Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1"); intent.addCategory("com.ispring.category.TEST1"); intent.addCategory("com.ispring.category.TEST2");
该intent对象之所以可以通过category测试是因为intent-filter包含了该intent对中所有的category值:com.ispring.category.TEST1”和com.ispring.category.TEST2。
以下intent对象无法通过category测试
Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1"); intent.addCategory("com.ispring.category.TEST1"); intent.addCategory("com.ispring.category.TEST3");
该intent之所以无法通过上面的intent-filter的category测试是因为intent-filter只包含了该intent中值为com.ispring.category.TEST1的category,而并未包含值为com.ispring.category.TEST3的category,不满足完全包含intent中全部category的情况。
intent对象不包含任何category
如果intent对象没有调用过addCategory()方法,那么intent对象就不包含任何的category。这种情形下,如果该intent不是用来启动Activity的话,那么无论intent-filter中category中如何配置,intent对象总是能通过intent-filter的category测试,即便intent-filter中没有声明任何的category,intent都能通过category测试。此处强调了该intent不是用来启动Activity这种条件,会在下面详细解释。
此处需要特别说明的是,我们在上面所有的示例中,都给Activity的intent-filter添加了值为android.intent.category.DEFAULT
的category,这是因为当我们把一个隐式的intent传递给startActivity()
或startActivityForResult()
方法时,Android会自动给该隐式intent添加值为android.intent.category.DEFAULT
的category,所以为了能让intent-filter包含intent中全部的category,我们就需要在Activity的intent-filter中添加该category,在使用时需要特别注意。
根据上面我们的几个示例,我们总结如下:
1. 如果intent对象不包含任何category,并且该intent不是用来启动Activity的,那么该intent对象总是能通过所有任意的intent-filter的category测试;
2. 如果intent对象包含category(至少一个),那么只有当intent-filter中声明的category全部包含intent对象中的所有category的时候才通过category测试。
3. 如果允许Activity被隐式的Intent启动,那么我们必须在该Activity的intent-filter中声明值为android.intent.category.DEFAULT
的category。
Data测试
为了指定可以接收的Intent的data,intent-filter需要声明0个多多个<data />
标签,例如:
<intent-filter>
<data android:mimeType="video/mpeg" android:scheme="http" ... />
<data android:mimeType="audio/mpeg" android:scheme="http" ... />
...
</intent-filter>
每个<data />
标签都可以指定一个URI结构以及data的MIME类型。一个完整的URI由scheme
、host
、port
和path
组成,其结构如下所示:
<scheme>://<host>:<port>/<path>
其中scheme既可以是Android中常见的协议,也可以是我们自定义的协议。Android中常见的协议包括content协议、http协议、file协议等,自定义协议可以使用自定义的字符串。
如下是一个content协议的URI:
content://com.example.project:200/folder/subfolder/etc
在该URI中,scheme是content,host是com.example.project,port是200,path是folder/subfolder/etc。如下是一个自定义协议的URI:
ispring://blog.csdn.net/sunqunsunqun
在该URI中,scheme是ispring,host是blog.csdn.net,没有明确设定port,path是sunqunsunqun。
组成URI的这些属性在<data />
标签中都是可选的 ,但存在如下的依赖关系:
- 如果没有指定scheme,那么host参数会被忽略
- 如果没有指定host,那么port参数会被忽略
- 如果scheme和host都没有指定,path参数会被忽略
当我们将intent对象中的Uri参数与intent-filter中的<data />
标签指定的URI格式进行对别时,我们我们只对比intent-filter的<data />
标签指定的部分,例如:
- 如果intent-filter中只指定了scheme,那么所有带有该sheme的URI都能匹配到该intent-filter。
- 如果intent-filter中只指定了scheme和authority(authority包括host和port两部分)而没有指定path,那么所有具有相同scheme和authority的URI都能匹配到该intent-filter,而不用考虑path为何值。
- 如果intent-filter中同时指定了scheme、authority和path,那么只有具有相同scheme、authority和path的URI才能匹配到该intent-filter。
需要注意的是,intent-filter的<data />
标签在指定path的值时,可以在里面使用通配符*
,起到部分匹配的效果。
data测试需要同时将intent对象中的URI、MIME类型与intent-filter的<data />
标签中指定的URI、MIME类型进行对比。
我们知道一个intent-filter下可以有多个<data />
标签,intent对象无需通过所有的<data />
标签测试,一般情况下,我们的intent对象只需通过了其中一个<data />
标签的测试并满足某些特定情形下的一些条件,那么该intent对象就通过了该intent-filter的data测试。
进行对比的规则分以下几种情况:
intent对象不包含URI和MIME类型
这种情况下,只有当intent-filter也没有指定任何URI和MIME类型的时候才能通过data测试。
例如我们有如下intent对象:Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1");
上面的intent对象可以通过下面的intent-filter的data测试:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter>
上面的intent对象无法通过下面的intent-filter测试:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" /> </intent-filter>
intent对象包含URI但不包含MIME类型
这种情况下,只有当intent对象的URI匹配到了intent-filter中的URI格式,并且intent-filter没有指定MIME类型的时候才能通过data测试。需要注意的是,这里所说的intent-filter没有指定MIME类型的情形指的是intent-filter中所有的<data />
标签都没有指定MIME类型,即整个intent-filter中完全没有android:mimeType
这几个字,理解这点很重要,大家在下面的几个示例中可以体会到这点。
例如有如下intent对象:Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1"); Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun"); intent.setData(uri);
上面的intent能通过如下的intent-filter的data测试:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> </intent-filter>
上面的intent对象可以通过以下intent-filter的data测试:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> <data android:scheme="sunqun" android:host="8080" /> </intent-filter>
intent对象虽然不能通过scheme为sunqun的
<data />
标签测试,但是可以通过scheme为ispring的data标签测试,且intent对象和intent-filter中的两个<data />
标签都没有指定MIME,所以上面的intent对象可以通过该intent-filter测试。上面的intent对象无法通过以下intent-filter的
<data />
标签测试:<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" android:scheme="ispring" android:host="blog.csdn.net" /> </intent-filter>
上面的intent对象之所以不能通过intent-filter中唯一的一个
<data />
标签测试是因为我们的intent对象没有指定MIME类型,但是上面的<data />
标签通过android:mimeType="text/plain"
设置了MIME类型。上面的intent对象无法通过以下intent-filter的data测试:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> <data android:mimeType="text/plain" /> </intent-filter>
上面的intent对象之所以无法通过该intent-filter中的data测试,是因为intent对象没有设置MIME类型,但是intent-filter中第二个data标签通过
android:mimeType="text/plain"
设置了MIME类型。
intent对象包含MIME类型但不包含URI
这种情况下,只有当intent中的MIME类型与intent-filter中列出的MIME类型相同,并且intent-filter没有指定任何的URI格式的时候才能通过data测试。需要注意的是,这里所说的intent-filter没有指定任何的URI格式的情形指的是intent-filter中所有<data />
标签都没有指定URI,即整个intent-filter中完全没有android:scheme
、android:host
、android:port
以及android:path
,理解这点很重要,大家在下面的几个示例中可以体会到这点。
例如有如下intent对象:Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1"); intent.setType("text/plain");
上面的intent对象可以通过以下intent-filter的data测试:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter>
上面的intent对象可以通过下面的intent-filter的data测试:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="image/*" /> <data android:mimeType="text/plain" /> </intent-filter>
上面的intent对象虽然没有通过MIME类型为
image/*
的第一个data标签测试,但能通过第二个data标签测试,并且intent对象和intent-filter都没有指定任何的URI格式。上面的intent对象不能通过以下intent-filter中的data测试:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" android:scheme="ispring" /> </intent-filter>
上面的intent对象中没有设置URI信息,但是在该intent-filter中设置了URI中的scheme值,所以intent无法通过intent-filter的data测试。
上面的intent对象无法通过以下intent-filter中的data测试:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> <data android:scheme="ispring" /> </intent-filter>
上面的intent对象没有指定URI信息,但是上面的intent-filter中第二个
<data />
标签设置了URI中的scheme信息,所以intent对象无法通过该intent-filter的data测试。
intent对象同时包含URI和MIME类型
这种情况下,要分别测试URI以及MIME类型测试是否通过,只有URI以及MIME测试都通过了,data测试才能通过。- 对于MIME测试:如果intent的MIME类型能够匹配intent-filter中列出的某一个
<data />
标签中的MIME类型值,那么MIME类型测试就通过了。 - 对于URI测试:
又细分两种情况,满足下面的任何一种情况都可以通过URI测试。- 如果intent的URI格式能够匹配intent-filter中列出的某一个
<data />
中的URI,那么URI测试就通过了。 - 如果intent的URI是
content:
协议或file:
协议,并且整个intent-filter的所有<data />
标签中都没有指定URI,那么该intent也能通过URI测试。换句话说,如果一个intent-filter只列出了MIME类型,没有列出任何URI相关的格式的话,那么这个intent-filter就默认是支持content:
协议或file:
协议的。
- 如果intent的URI格式能够匹配intent-filter中列出的某一个
下面举几个例子大家自己体会一下。
假设有如下协议为自定义协议
ispring:
的intent对象:Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1"); Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun"); String type = "text/plain"; intent.setDataAndType(uri, type);
上面的intent对象可以通过下面的intent-filter的data测试:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> <data android:mimeType="text/plain" /> </intent-filter>
上面的intent对象无法通过下面的intent-filter的data测试:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" android:port="8080" /> <data android:mimeType="text/plain" /> </intent-filter>
port不满足,URI测试不通过,导致data测试失败。
上面的intent对象无法通过下面的intent-filter的data测试:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" android:host="blog.csdn.net" /> <data android:mimeType="image/*" /> </intent-filter>
android:mimeType
不满足,MIME类型测试不通过,导致data测试失败。
假设有如下协议为
content:
的intent对象:Intent intent = new Intent(); intent.setAction("com.ispring.action.ACTION_TEST1"); Uri uri = Uri.parse("content://com.ispring.test"); String type = "text/plain"; intent.setDataAndType(uri, type);
上面的intent对象无法通过下面的intent-filter的data测试:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="ispring" /> <data android:mimeType="text/plain" /> </intent-filter>
URI中的scheme不匹配,导致URI测试不通过,导致data测试失败。
上面的intent对象可以通过下面的intent-filter的data测试:
<intent-filter> <action android:name="com.ispring.action.ACTION_TEST1" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter>
intent中使用的是
content:
协议,并且整个intent-filter中都没有定义URI格式,所以URI测试是可以通过的,并且MIME类型能找到匹配项,所以可以通过data测试。
- 对于MIME测试:如果intent的MIME类型能够匹配intent-filter中列出的某一个