服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Android - 详解Android中Intent对象与Intent Filter过滤匹配过程

详解Android中Intent对象与Intent Filter过滤匹配过程

2021-04-22 17:35孙群 Android

这篇文章主要介绍了Android中Intent对象与Intent Filter过滤匹配过程,感兴趣的小伙伴们可以参考一下

如果对intent不是特别了解,可以参见博文《详解android中intent的使用方法》,该文对本文要使用的action、category以及data都进行了详细介绍。如果想了解在开发中常见intent的使用,可以参见《android中intent习惯用法》。

本文内容有点长,希望大家可以耐心读完。

本文在描述组件在manifest中注册的intent filter过滤器时,统一用intent-filter表示。

一、概述

我们知道,intent是分两种的:显式intent和隐式intent。如果一个intent明确指定了要启动的组件的完整类名,那么这个intent就是显式intent,否则就是隐式intent。当我们用一个显式intent去启动组件时,android会根据intent对象所提供的component name直接找到要启动的组件,当我们用一个隐式的intent去启动组件时,android系统就无法直接知道要启动的组件名称了,本文就是讲解android系统如何根据隐式intent查找匹配到要启动的组件。

当android系统接收到一个隐式intent要启动一个activity(或其他组件)时,android会根据以下三个信息比较intent的信息与注册的组件的intent-filter的信息,从而为该intent选择出最匹配的activity(或其他组件):

  • intent中的action
  • intent中的category
  • intent中的data(包含uri以及data的mime类型)

也就是隐式intent对象要满足要启动的目标组件中注册的intent-filter中的<action />、<category />、<data />三个标签中的信息,即要分别通过action测试、category测试以及data测试。intent-filter信息是在android的manife文件中描述的,顾名思义,intent-filter是intent过滤器,就是用来过滤intent的。

如果隐式intent对象同时通过了某个组件的中intent-filter的action测试、category测试以及data测试,那么该组件就可以被intent对象所启动。如果隐式intent对象没有通过系统中任何组件的intent-filter测试,那么就没有android系统无法找到该intent对象要启动的组件。下面我们依次看一下如何才能通过这三个测试。

二、action测试

为了指定能够接收并处理的intent的类型,组件可以在intent-filter中声明其支持0个或多个action,例如:

 

?
1
2
3
4
5
<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如下所示:

?
1
2
3
4
5
6
<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测试:

?
1
2
3
4
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测试:

?
1
2
3
4
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对象如下所示:

?
1
2
3
4
5
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:

?
1
2
3
4
5
<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:

?
1
2
3
4
<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,例如:

?
1
2
3
4
5
<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如下所示:

?
1
2
3
4
5
6
<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测试

?
1
2
3
4
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测试

?
1
2
3
4
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 />标签,例如:

?
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>

每个<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对象:

?
1
2
intent intent = new intent();
intent.setaction("com.ispring.action.action_test1");

上面的intent对象可以通过下面的intent-filter的data测试:

?
1
2
3
4
<intent-filter>
 <action android:name="com.ispring.action.action_test1" />
 <category android:name="android.intent.category.default" />
</intent-filter>

上面的intent对象无法通过下面的intent-filter测试:

?
1
2
3
4
5
<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对象:

?
1
2
3
4
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测试:

?
1
2
3
4
5
<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测试:

?
1
2
3
4
5
6
<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 />标签测试:

?
1
2
3
4
5
<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测试:

?
1
2
3
4
5
6
<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对象:

?
1
2
3
intent intent = new intent();
intent.setaction("com.ispring.action.action_test1");
intent.settype("text/plain");

上面的intent对象可以通过以下intent-filter的data测试:

?
1
2
3
4
5
<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测试:

?
1
2
3
4
5
6
<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测试:

?
1
2
3
4
5
<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测试:

?
1
2
3
4
5
6
<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:协议的。
下面举几个例子大家自己体会一下。

假设有如下协议为自定义协议ispring:的intent对象:

?
1
2
3
4
5
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测试:

?
1
2
3
4
5
6
<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测试:

?
1
2
3
4
5
6
<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测试:

?
1
2
3
4
5
6
<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对象:

?
1
2
3
4
5
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测试:

?
1
2
3
4
5
6
<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测试:

?
1
2
3
4
5
<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测试。

综上,我们就完成了对intent中action、category、data测试的详细解释,本文所有示例代码均在android studio 1.0正式版中验证过没有问题。很感谢大家能够耐心读完本博文,希望本文对大家正确使用intent过滤器有所帮助。

延伸 · 阅读

精彩推荐