首页 > 代码库 > 安卓 Intent 与/intent-filter的关系详解。

安卓 Intent 与/intent-filter的关系详解。

Intent可以分为两种:显式Intent隐式Intent;
显式Intent:通过组件名字字段指定目标组件;因为开发者通常不知道其它应用程序的组件名字,所以,显式Intent通常用于应用程序内部消息传递;例如:一个Activity启动从属的服务或启动一个同级别的Activity;
隐式Intent:不指定目标组件的名字(组件名字字段是空);隐式Intent经常用于激活其它应用程序中的组件;
Android系统传递一个显式Intent消息对象到一个指定目标组件名字的实例,Intent消息对象中只用组件名字内容区决定哪个组件应该获得这个Intent消息对象,而不用其它内容;
对于隐式Intent消息对象来说,需要指定另外一种不同的策略;由于没有指定目标组件名字,Android系统就必须查找并匹配一个最合适的组件或多个合适的组件来处理这个Intent消息对象:一个Activity或Service去执行请求动作,或一组广播接收者去响应消息;为了打到这个目的,Android定义了一个名为IntentFilter的Intent过滤器类来完成这个任务;IntentFilter类的实例通过比较Intent消息对象中的内容和Intent过滤器来实现目标组件的查找和匹配;每个IntentFilter实例描述了该组件所能响应Intent消息对象的能力:组件希望接收什么类型的请求行为,什么类型的请求数据,等等;Intent过滤器实例关联到潜在的接收Intent消息对象的组件,它声明了目标组件响应Intent消息对象的能力,界定了目标组件所能处理的Intent消息对象,它们打开组件接收声明的Intent消息对象类型的隐式Intent;如果一个组件没有关联任何Intent过滤器,则它只能接收显式Intent消息对象,而声明了Intent过滤器的组件则可以接收显式的和隐式的Intent消息对象;
IntentFilter实例与隐式Intent匹配时需要比较的三要素是:动作Action数据(包括URI和MIME类型)和Category种类;而附加信息Extras和标志Flags在查找匹配时不起作用;实际上,一个隐式Intent消息对象如要能够成功地传递给目标组件,就必须要通过这三个方面的检查和匹配,如果有任何一方面不匹配,Android系统都不会将该隐式Intent消息对象传递给目标组件;
Activity、Service和BroadcastReceiver为了告诉系统能够处理哪些隐式Intent,它们可以有一个或多个Intent过滤器实例;每个过滤器描述组件的一种能力,即,感兴趣的一组Intent消息对象;实际上,它筛选掉不感兴趣的Intent,也仅仅是不想要的隐式Intent消息对象;一个显式Intent消息对象总是能够成功地传递到目标组件,不管它包含什么,也不考虑过滤器,不会进行任何查找和匹配;但是一个隐式Intent消息对象,仅当它能够通过组件的过滤器之后,才能成功地传递给目标组件;
一个目标组件能够做的每一件工作都有独立的过滤器;每个过滤器都有对应的IntentFilter类的实例;因为Android系统在启动一个组件之前都必须要知道这个组件的能力,但是Intent过滤器通常不在Java代码里面设置,而是在应用程序的清单文件AndroidManifest.xml中的标签<intent-filter>中设置;但是,有一个例外,广播消息接收者的过滤器可以通过函数Context.registerReceiver()动态地注册,它直接创建一个IntentFilter类的实例;
一个过滤器有对应的动作Action、数据Data和种类Category;过滤器要检查隐式Intent消息对象的所有这三个字段,其中任何一个字段匹配失败,则Android系统都不会把这个隐式Intent消息对象传递给目标组件;然而因为一个组件可以有多个Intent过滤器,所以,一个隐式Intent消息对象虽然不能通过一个过滤器的检查,但是有可能通过另外一个过滤器的检查;
一、动作检查
清单文件中的标签<intent-filter>使用子标签<action>来列出动作Action;例如:
<intent-filter ...>
  <action android:name="com.test.project.ADD_BOOK" />
  <action android:name="com.test.project.EDIT_BOOK" />
  <action android:name="com.test.project.DELETE_BOOK" />
  ......
</intent-filter>
一个Intent消息对象仅描述一个动作,而一个过滤器中可以指定多个动作,这个动作列表不能为空,一个过滤器至少要包含一个<action>子标签,否则它将阻塞所有的Intent消息对象;
如要通过检查,Intent消息对象中的动作Action必须要匹配过滤器的动作列表中的一个;如果Intent消息对象或者过滤器的动作列表中没有指定任何一个具体的动作Action,则会出现以下两种情况:
1、如果过滤器没有指定任何一个动作Action,则所有的Intent消息对象都不会匹配成功,所有的Intent消息对象都将匹配失败,即,没有任何一个Intent消息对象能够通过过滤器;
2、如果Intent消息对象没有指定任何一个动作Action,将自动通过过滤器的检查(只要过滤器的动作列表中至少有一个动作,否则就是上面的情况了);
二、种类检查
清单文件中的标签<intent-filter>使用子标签<category>来列出种类Category;例如:
<intent-filter ...>
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  ......
</intent-filter>
一个Intent消息对象如要通过种类检查,则这个Intent消息对象中的每个种类都必须匹配过滤器的种类列表中的一个;即,过滤器能够列出额外的种类,但Intent消息对象中的每个种类都必须能够在过滤器的种类列表中找到,只要有一个种类在过滤器的种类列表中没有找到,则种类检测就认为是失败的;
因此,原则上,如果一个Intent消息对象中没有指定种类,即,种类字段为空,那么它总是应该通过过滤器的种类检查,而不管过滤器中有什么种类;但有个例外,Android系统对待所有传递给Context.startActivity()函数的隐式Intent消息对象,它们至少都要包含种类"android.intent.category.DEFAULT"(对应CATEGORY_DEFAULT常量);因此,Activity如要接收隐式Intent消息对象,就必须要在Intent过滤器中包含种类"android.intent.category.DEFAULT";
备注:种类"android.intent.action.MAIN"标记Activity开始新的任务,种类"android.intent.category.LAUNCHER"标识启动HOME界面;它们可以包含种类"android.intent.category.DEFAULT"到种类类表,也可以不包含;
三、数据检查
清单文件中的标签<intent-filter>使用子标签<data>来列出数据Data;例如:
<intent-filter ...>
  <data android:mimeType="video/mpeg" android:scheme="http" .../>
  <data android:mimeType="audio/mpeg" android:scheme="http" .../>
  ......
</intent-filter>
每个<data>标签都包含一个数据的URI和数据的MIME类型;它有四个属性:scheme、host、port和path,分别对应URI的每个部分;
格式如下:
scheme://host:port/path
host和port共同构成URI的凭据(authority),如果不指定host,则port也会被忽略;这四个属性都都是可选的,它们之间并不都是我完全独立的;如果要让authority有意义,则scheme必须要指定;要让path有意义,scheme和authority都必须要指定;
当检查匹配Intent消息对象和过滤器的URI时,只比较过滤器中出现的URI属性;例如,如果一个过滤器仅指定了scheme,则所有此scheme的URIs都能被过滤器成功匹配;如果一个过滤器指定了scheme和authority,但是没有指定path,则所有匹配scheme和authority的URIs都能被过滤器成功匹配,而不考虑它们的path;如果四个属性都指定了,则都要匹配成功才算通过;然而过滤器中的path可以包含通配符来要求匹配path中的一部分;
<data>标签的type属性指定数据的MIME类型;Intent消息对象和过滤器实例都可以使用通配符"*"来匹配子类型字段;例如:"text/*"、"audio/*"表示任何子类型;
数据检查既要检查URI,也要检查数据类型,规则如下:
规则1、一个Intent消息对象既不包含URI,也不包含数据类型:仅当过滤器也不指定任何URI和数据类型时,才不能通过检查;否则都能通过;
规则2、一个Intent消息对象包含URI,但不包含数据类型:仅当过滤器也不指定任何数据类型,同时匹配它们的URI,才能通过检查;
规则3、一个Intent消息对象包含数据类型,但不包含URI:仅当过滤器也只指定数据类型,且与Intent消息对象的相同,才能通过检查;
规则4、一个Intent消息对象既包含URI,也包含数据类型:数据类型部分,只有与过滤器中指定的数据类型之一匹配,才算通过检查;URI部分,它的URI要出现在过滤器中,或者它有"content:"或"file:"URI,又或者过滤器没有指定URI;换句话说,如果它的过滤器仅列出了数据类型,组件假定支持"content:"或"file:";
如果一个Intent消息对象能够通过多个Activity或Service的过滤,则用户可能会被问:哪个组件被激活?如果没有找到目标组件,则会产生一个异常;
四、通用情况
上面最后一条规则表明组件能够从文件或者是内容提供者那里获取本地数据;因此,它们的过滤器仅列出数据类型且不必明确指出"content:"或"file:"的scheme的名字;
者是一种典型情况,一个<data>标签像下面这样:
<data android:mimeType="image/*" />
这个配置告诉Android系统,这个组件能够从内容提供者那里获取image数据并显示它;因为大部分可用数据由内容提供者分发,过滤器指定一个数据类型但没有指定URI,或许最通用;
另一种通用配置是过滤器指定一个scheme和一个数据类型;例如:
<data android:scheme="http" android:type="video/*" />
这个配置告诉Android系统,这个组件能够从网络获取视频数据并显示它;