更多原文请见: http://mobile.51cto.com/abased-349323.htm
 
 
Intent是Android应用程序核心组件之间通信和传递信息的核心机制。与之相关的IntentFilter也具有相关的安全机制(测试)来进行约束。本文将对其进行详细介绍。
 
一、Intent和IntentFilter简介
一个应用程序的三个核心组件(活动,服务和广播接收器)都是通过消息即意图(Intents)来激活的。Intent消息传送是相同或不同应用中组件运行时晚绑定的一种机制。意图本身(一个意图对象)是一个包含被执行操作抽象描述的被动的数据结构。或,对于广播而言,是某件已经发生并被声明的事情的描述。存在如下几种不同的机制来传送意图到每种组件中:
  • 一个意图对象是传递给Context.startActivity()或者Activity.startActivityForResult()来启动一个活动或者让一个存在的活动去做某些新的事情。
  • 一个意图对象是传递给Context.startService()来发起一个服务或者递交新的指令给运行中的服务。类似的,一个意图能被传递给Context.bindService() 来在调用组件和一个目标服务之间建立连接。作为一个可选项,它可以发起这个服务如果还没运行的话。
  • 传递给任意广播方法(例如Context.sendBroadcast(),Context.sendOrderedBroadcast(),或者Context.sendStickyBroadcast())的意图对象被传递给所有感兴趣的广播接收者。许多种广播产生于系统代码。
在每个例子里,Android系统找到合适的活动、服务或者一组广播接收者来回应这个意图,必要时实例化它们。这些消息传送系统没有重叠:广播意图仅被传递给广播接收者,永远不会给活动或者服务。一个传送给startActivity()的意图是只会被传递给一个活动,永远不会给一个服务或广播接收者,如此类推。
为了通知系统它们可以处理哪些意图,活动、服务和广播接收器可以有一个或多个意图过滤器。每个过滤器描述组件的一个能力,一系列组件想要接收的意图。它实际上按照一个期望的类型来进行意图滤入,同时滤出不想要的意图-但是只有不想要的隐式意图会被滤出(那些没有命名目标的对象类)。一个显式意图总能够被递交给它的目标,而无论它包含什么。这种情况下过滤器不起作用。但是一个显式意图仅当它能通过组件的一个过滤器时才可以被递交到这个组件。
组件为它能做的每项工作,每个呈现给用户的不同方面分有不同的过滤器。比如,范例记事本应用程序中的主要活动有三个过滤器:一个是空白板,另一个是用户可以查看、编辑、或选择的一个指定的记事目录,第三是在没有初始目录说明的情况下查找一个特定的记录。一个意图过滤器是 IntentFilter类的一个实例。但是,由于Android系统在启动一个组件前必须知道这个组件的能力,意图过滤器通常不会用Java代码来设置,而是在应用程序清单文件(AndroidManifest.xml)中设置元素。(有一个例外,通过调用 Context.registerReceiver()来注册的广播接收器的过滤器;它们是作为意图过滤器对象而被直接创建的。
 
 
二 、过滤器与安全Filters and security
我们不能信赖一个意图过滤器的安全性。当它打开一个组件来接收某些特定类型的隐式意图,它并不能阻止以这个组件为目标的显式意图。即使过滤器对组件要处理的意图限制某些动作和数据源,总有人能把一个显式意图和一个不同的动作及数据源组合在一起,然后命名该组件为目标。
一个过滤器和意图对象有同样的动作、数据以及类别字段。一个隐式意图在过滤器的所有三个方面都被测试。为了递交到拥有这个过滤器的组件,它必须通过所有这三项测试。即便只有一个不通过,Android系统都不会把它递交给这个组件。不过,由于一个组件可以包含多个意图过滤器,一个不能通过其中一个组件过滤器的意图可能在另外的过滤器上获得通过。
与之相关的三个测试详细描述如下 :
测试一:动作测试(Actiontest
清单文件中的意图过滤器元素里列举了动作元素,比如:
   
   
   
   
  1. <intent-filter. . . >
  2. <action android:name="com.example.project.SHOW_CURRENT" />
  3. <action android:name="com.example.project.SHOW_RECENT" />
  4. <action android:name="com.example.project.SHOW_PENDING" />
  5. . . .
  6. intent-filter>
如同例子所示,一个意图对象只对单个动作命名,而一个过滤器可能列举多个。列表不能为空;一个过滤器必须包含至少一个动作元素,否则它将阻塞所有的意图。
为了通过这个测试,在意图对象中指定的动作必须匹配过滤器中所列举的动作之一。如果意图对象或过滤器不指定一个动作,结果将是:如果这个过滤器没有列出任何动作,那意图就没有什么可匹配的,因此所有的意图都会测试失败。没有意图能够通过这个过滤器。另一方面,一个未指定动作的意图对象自动通过这个测试-只要过滤器包含至少一个动作。
测试二:类别测试(Categorytest
一个意图过滤器元素也列举了类别作为子元素。比如:
   
   
   
   
  1. <intent-filter. . . >
  2. <category android:name="android.intent.category.DEFAULT" />
  3. <category android:name="android.intent.category.BROWSABLE" />
  4. . . .
  5. intent-filter>
注意前面描述的动作和类别常量没有在清单文件中使用。相反使用了完整的字符串。比如,对应于前述CATEGORY_BROWSABLE常量,上面的例子里使用了"android.intent.category.BROWSABLE"字符串。类似的,字符串"android.intent.action.EDIT" 对应于ACTION_EDIT常量。
对一个通过类别测试的意图,每个意图对象中的类别必须匹配一个过滤器中的类别。这个过滤器可以列举另外的类别,但它不能遗漏任何在这个意图中的类别。
因此,原则上一个没有类别的意图对象应该总能够通过测试,而不管过滤器里有什么。绝大部分情况下这个是对的。但有一个例外,Android把所有传给 startActivity()的隐式意图当作他们包含至少一个类别:"android.intent.category.DEFAULT"(CATEGORY_DEFAULT常量)。 因此,想要接收隐式意图的活动必须在它们的意图过滤器中包含"android.intent.category.DEFAULT"。,而带"android.intent.action.MAIN"和"android.intent.category.LAUNCHER"设置的过滤器是例外。它们标记那些启动新任务和呈现在启动屏幕的活动。它们可以在类别列表中包含"android.intent.category.DEFAULT",但不是必要的。
测试三:数据测试(Data test
就像动作和类别,一个意图过滤器的数据规格被包含在一个子元素中。而且这个子元素可以出现多次或一次都不出现。例如:
   
   
   
   
  1. <intent-filter. . . >
  2. <data android:type="video/mpeg" android:scheme="http" .. . />
  3. <data android:type="audio/mpeg" android:scheme="http" .. . />
  4. . . .
  5. intent-filter>
每个数据元素可以指定一个URI和一个数据类型(MIME媒体类型)。有一些单独的属性-模式,主机,端口和路径-URI的每个部分:
scheme://host:port/path
比如,在下面的URI里面,
content://com.example.project:200/folder/subfolder/etc
模式是"内容",主机是"com.example.project",端口是"200",路经是"folder/subfolder/etc"。主机和端口一起组成URI鉴权(authority);如果未指定主机,端口会被忽略。
这些属性都是可选的,但彼此有依赖关系:一个授权要有意义,必须指定一个模式。一个路经要有意义,必须同时指定模式和鉴权。
当一个意图对象中的URI被用来和一个过滤器中的URI规格比较时,它实际上比较的是上面提到的URI的各个部分。比如,如果过滤器仅指定了一个模式,所有那个模式的URIs和这个过滤器相匹配;如果过滤器指定了一个模式、鉴权但没有路经,所有相同模式和鉴权的URIs可以匹配上,而不管它们的路经;如果过滤器指定了一个模式、鉴权和路经,只有相同模式、鉴权和路经的URIs可以匹配上。当然,一个过滤器中的路径规格可以包含通配符,这样只需要部分匹配即可。
数据元素的类型属性指定了数据的MIME类型。这在过滤器里比在URI里更为常见。意图对象和过滤器都可以使用一个"*"通配符指定子类型字段-比如,"text/*"或者"audio/*"-指示任何匹配的子类型。
数据测试同时比较意图对象和过滤器中指定的URI和数据类型。规则如下:
1) 一个既不包含URI也不包含数据类型的意图对象仅在过滤器也同样没有指定任何URIs和数据类型的情况下才能通过测试。
2) 一个包含URI但没有数据类型的意图对象仅在它的URI和一个同样没有指定数据类型的过滤器里的URI匹配时才能通过测试。这通常发生在类似于mailto:和tel:这样的URIs上:它们并不引用实际数据。
3) 一个包含数据类型但不包含URI的意图对象仅在这个过滤器列举了同样的数据类型而且也没有指定一个URI的情况下才能通过测试。
4) 一个同时包含URI和数据类型(或者可从URI推断出数据类型)的意图对象可以通过测试,如果它的类型和过滤器中列举的类型相匹配的话。如果它的URI和这个过滤器中的一个URI相匹配或者它有一个内容content:或者文件file: URI而且这个过滤器没有指定一个URI,那么它也能通过测试。换句话说,一个组件被假定为支持content:和file: 数据如果它的过滤器仅列举了一个数据类型。
如果一个意图可以通过不止一个活动或服务的过滤器,用户可能会被询问要激活那个组件,并且,如果没有发现目标对象将会出现异常。