关闭

Android Intents和Intent过滤器(二)

标签: Android基础
318人阅读 评论(0) 收藏 举报
分类:

Action test

To specify accepted intent actions, an intent filter can declare zero or more <action> elements. For example:

<intent-filter>
    <action android:name="android.intent.action.EDIT" />
    <action android:name="android.intent.action.VIEW" />
    ...
</intent-filter>

To get through this filter, the action specified in the Intent must match one of the actions listed in the filter.

If the filter does not list any actions, there is nothing for an intent to match, so all intents fail the test. However, if an Intent does not specify an action, it will pass the test (as long as the filter contains at least one action).

Category test

To specify accepted intent categories, an intent filter can declare zero or more <category> elements. For example:

<intent-filter>
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    ...
</intent-filter>

For an intent to pass the category test, every category in the Intent must match a category in the filter. The reverse is not necessary—the intent filter may declare more categories than are specified in the Intent and theIntent will still pass. Therefore, an intent with no categories should always pass this test, regardless of what categories are declared in the filter.

Note: Android automatically applies the the CATEGORY_DEFAULT category to all implicit intents passed tostartActivity() and startActivityForResult(). So if you want your activity to receive implicit intents, it must include a category for "android.intent.category.DEFAULT" in its intent filters (as shown in the previous <intent-filter> example.

Data test

To specify accepted intent data, an intent filter can declare zero or more <data> elements. For example:

<intent-filter>
    <data android:mimeType="video/mpeg" android:scheme="http" ... />
    <data android:mimeType="audio/mpeg" android:scheme="http" ... />
    ...
</intent-filter>

Each <data> element can specify a URI structure and a data type (MIME media type). There are separate attributes — schemehostport, and path — for each part of the URI:

<scheme>://<host>:<port>/<path>

For example:

content://com.example.project:200/folder/subfolder/etc

In this URI, the scheme is content, the host is com.example.project, the port is 200, and the path isfolder/subfolder/etc.

Each of these attributes is optional in a <data> element, but there are linear dependencies:

  • If a scheme is not specified, the host is ignored.
  • If a host is not specified, the port is ignored.
  • If both the scheme and host are not specified, the path is ignored.

When the URI in an intent is compared to a URI specification in a filter, it's compared only to the parts of the URI included in the filter. For example:

  • If a filter specifies only a scheme, all URIs with that scheme match the filter.
  • If a filter specifies a scheme and an authority but no path, all URIs with the same scheme and authority pass the filter, regardless of their paths.
  • If a filter specifies a scheme, an authority, and a path, only URIs with the same scheme, authority, and path pass the filter.

Note: A path specification can contain a wildcard asterisk (*) to require only a partial match of the path name.

The data test compares both the URI and the MIME type in the intent to a URI and MIME type specified in the filter. The rules are as follows:

  1. An intent that contains neither a URI nor a MIME type passes the test only if the filter does not specify any URIs or MIME types.
  2. An intent that contains a URI but no MIME type (neither explicit nor inferable from the URI) passes the test only if its URI matches the filter's URI format and the filter likewise does not specify a MIME type.
  3. An intent that contains a MIME type but not a URI passes the test only if the filter lists the same MIME type and does not specify a URI format.
  4. An intent that contains both a URI and a MIME type (either explicit or inferable from the URI) passes the MIME type part of the test only if that type matches a type listed in the filter. It passes the URI part of the test either if its URI matches a URI in the filter or if it has a content: or file: URI and the filter does not specify a URI. In other words, a component is presumed to support content: and file: data if its filter lists only a MIME type.

This last rule, rule (d), reflects the expectation that components are able to get local data from a file or content provider. Therefore, their filters can list just a data type and do not need to explicitly name the content: andfile: schemes. This is a typical case. A <data> element like the following, for example, tells Android that the component can get image data from a content provider and display it:

<intent-filter>
    <data android:mimeType="image/*" />
    ...
</intent-filter>

Because most available data is dispensed by content providers, filters that specify a data type but not a URI are perhaps the most common.

Another common configuration is filters with a scheme and a data type. For example, a <data> element like the following tells Android that the component can retrieve video data from the network in order to perform the action:

<intent-filter>
    <data android:scheme="http" android:type="video/*" />
    ...
</intent-filter>

Intent matching

Intents are matched against intent filters not only to discover a target component to activate, but also to discover something about the set of components on the device. For example, the Home app populates the app launcher by finding all the activities with intent filters that specify the ACTION_MAIN action and CATEGORY_LAUNCHERcategory.

Your application can use intent matching in a similar way. The PackageManager has a set of query...()methods that return all components that can accept a particular intent, and a similar series of resolve...()methods that determine the best component to respond to an intent. For example, queryIntentActivities()returns a list of all activities that can perform the intent passed as an argument, and queryIntentServices()returns a similar list of services. Neither method activates the components; they just list the ones that can respond. There's a similar method, queryBroadcastReceivers(), for broadcast receivers.



Intent对象解析

Intent能够被分成两组:

1.   用组件的名称把Intent对象明确的指向目标组件(在Intent对象的组件名字段指定目标组件名)。因为一般情况下其他应用的开发者不会了解目标组件的名字,所以通常针对应用程序的内部消息使用明确命名的Intent对象,如一个Activity启动一个下属服务或启动一个姊妹Activity。

2.   没有命名目标(Intent对象的组件名字段是空的)的隐式的Intent对象。隐式的Intent对象经常被用于激活其他应用程序中的组件。

Android系统把一个明确命名的Intent对象发送给目标类的一个实例。除了组件名以外,不再用Intent对象内任何其他信息来判断哪个组件应该获得这个Intent对象。

对于隐式的Intent对象,需要不同的分类。在缺少目标组件的情况下,Android系统必须查找最适合的组件(一个能够执行请求动作的Activity或Service,或者是一组能够响应广播通知的Broadcast Receiver)来处理这个Intent对象。系统通过把Intent对象的内容跟Intent过滤器比较,跟组件的结构关联就能够潜在接收Intent对象。过滤器会公开组件的能力和它能够处理的Intent对象限制。它们打开组件接收可能的公开类型的隐式Intent对象。如果组件没有任何过滤器,那么它仅能接收明确命名的Intent对象。带有过滤器的组件能够接收命名和匿名的Intent对象。

Intent过滤器会参考Intent对象以下三个方面来检测是否接收这个Intent对象:

1.   动作(action)

2.   数据(URI和数据类型)

3.   分类(category)

附加信息(extras)和标记(flags)不作为判断哪个组件接收这个Intent对象标准。

Intent过滤器

Intent过滤器是用来通知系统它们能够处理那种类型隐式的Intent对象,Activity、Service、Broadcast Receiver能够有一个或多个Intent过滤器。每个过滤器都描述了组件的一种能力,说明了组件将会接受的Intent对象集。它滤如有效的期望类型的Intent对象,滤出不想要的Intent对象---但是仅是不想要的隐式Intent对象(那些没有命名目标类的Intent对象)。一个有明确命名的Intent对象总是包被发送给它的目标类的实例,而不管它包含了什么;过滤器不起作用。但是隐式的Intent对象仅能发送给能够通过组件的一个过滤器来传递它的一个组件。

对于组件能够做的每项工作,它都会有一个独立的过滤器,并且每一工作都能够展现给用户。例如,实例应用Note Pad应用程序的NoteEditor Activity就有两个过滤器---一个是用于启动带有用户能够查看或编辑的特定注释信息的过滤器;另一个是用于启动一个新的,用户能够填充和保存的空白注释过滤器。(Note Pad的所有过滤器在“Note Pad示例”一节中介绍)

一个Intent过滤器是IntentFilter类的实例。但是因为Android系统在它启动组件之前必须了解有关组件的能力,所以Intent过滤器通常都不是用Java代码来建立的,而是在应用程序的清单文件(AndroidManifest.xml)中用<intent-filter>元素来声明。(通过调用Context.registerReceiver()方法来动态注册的Broadcast Receiver是一个例外,它们直接创建IntentFilter对象做为过滤器。)

过滤器有类似于Intent对象的动作、数据、和分类的字段,过滤器会用这三个域来检测一个隐式的Intent对象。对于要传递给拥有过滤器的组件的Intent对象,必须传递所有的这三个要检测的字段。如果其中之一失败了,Android系统也不会把它发送给对应的组件---至少在基于那个过滤器的基础上不会发送。但是,因为一个组件能够有多个Intent过滤器,即使不能通过组件的一个过滤器来传递Intent对象,也可以使用其他的过滤器。

以下详细说明对三个域的检测

1.   动作域检测

在清单文件中的<intent-filter>元素内列出对应动作的<action>子元素。如:

<intent-filter . . . >
    <action android:name="com.example.project.SHOW_CURRENT" />
    <action android:name="com.example.project.SHOW_RECENT" />
    <action android:name="com.example.project.SHOW_PENDING" />
    . . .
</intent-filter>

像上例显示的那样,一个Intent对象就是一个命名动作,一个过滤器可以列出多个动作。这个列表不能是空的,一个过滤器必须包含至少一个<action>元素,否则它会阻塞所有的Intent对象。

要通过这个检测,在Intent对象中指定的动作必须跟这个过滤器的动作列表中动作一致匹配。如果Intent对象或过滤器没有指定的动作,会产生以下结果:

A.  如果对列表中所有动作都过滤失败,那么对于要匹配的Intent对象不做任何事情,而且所有的其他Intent检测都失败。没有Intent对象能够通过这个过滤器;

B.  另一方面,没有指定动作的Intent对象会自动的通过检测---只要这个过滤器包含至少一个动作。

2.  分类域检测

<intent-filter>元素也要列出分类作为子元素。例如:

<intent-filter . . . >
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    . . .
    </intent-filter>

注意,对于清单文件中的动作和分类没有使用早先介绍的常量,而是使用了完整字符串值来替代。例如,上例中“android.intent.category.BROWSABLE”字符串对应本文档前面提到的CATEGOR_BROWSABLE常量。类似的“android.intent.action.EDIT”字符串对应ACTION_EDIT常量。

对于一个要通过分类检测的Intent对象,在Intent对象中每个分类都必须跟过滤器中的一个分类匹配。过滤器能够列出额外的分类,但是它不能忽略Intent对象中的任何分类。

因此,原则上一个没有分类的Intent对象应该始终通过这个检测,而不管过滤器中声明的分类。大多数情况都是这样的,但是,有一个例外,Android处理所有传递给startActivity()方法的隐式Intent对象,就像它们至少包含了一个“android.intent.category.DEFAULT(对应CATEGORY_DEFAULT常量)”分类一样。因此接收隐式Intent对象的Activity必须在它们的Intent过滤器中包含“android.intent.category.DEFAULT”分类。(带有“android.intent,action.MAIN”和“android.intent.category.LAUNCHER”设置的过滤器是个例外。因为它们把Activity标记为新任务的开始,并且代表了启动屏。它们能够在分类列表中包含“android.intent.category.DEFAULT”,但是不需要。)

3.  数据域检测

像动作分类检测一样,针对Intent过滤器的数据规则也要包含在一个子元素中,并且,跟动作和分类的情况一样,这个子元素也能够出现多次,或者不出现。例如:

<intent-filter . . . >
    <data android:mimeType="video/mpeg" android:scheme="http" . . . /> 
    <data android:mimeType="audio/mpeg" android:scheme="http" . . . />
    . . .
</intent-filter>

每个<data>元素能够指定一个URI和一个数据类型(MIME媒体类型)对于每个URI部分都会有独立的属性---scheme、host、port、path:scheme://host:port/path

例如,以下URI:

content://com.example.project:200/folder/subfolder/etc

scheme是content,host是“com.example.project”,port是“200”,path是“folder/subfolder/etc”。host和port一起构成了URI授权,如果没有指定host,那么port也会被忽略。

这些属性是可选的,但是,它们不是彼此独立的,如一个授权意味着必须指定一个scheme,一个path意味着必须指定scheme和授权。

当Intent对象中的URI跟过滤器的一个URI规则比较时,它仅比较在过滤器总实际提到的URI部分。如,如果一个过滤器仅指定了一个scheme,那么带有这个scheme的所有的URIs都会跟这个过滤器匹配。如果一个过滤器指定了一个scheme和授权,但是没有路径,那么带有相同scheme和授权的所有URIs的Intent对象都会匹配,而不管它们的路径。如果一个过滤器指定了一个scheme、授权、和路径,那么就只有相同的scheme、授权和路径Intent对象才会匹配。但是,在过滤器中的路径规则能够包含只要求路径部分匹配的通配符。

<data>元素的type属性指定了数据的MIME类型。它在过滤器中比URI更共同。对于子类型域,Intent对象和过滤器都能够使用“*”通配符---例如,“text/*”或“audio/*”指明可以跟任意子类型匹配。

数据检测会比较Intent对象和过滤器中的URI和数据类型。规则如下:

A.只有过滤器没有指定任何URI或数据类型的情况下,既没有URI也没有数据类型的Intent对象才能通过检测;

B.一个包含URI但没有数据类型的Intent对象(并且不能从URI中推断出数据类型)只有跟过滤器中的一个URI匹配,并且同样这个过滤器没有指定数据类型时,才能通过检测。这种情况仅针对不指向实际数据的URIs,如mailto:和tel:。

C.一个包含了数据类型但不没有URI的Intent对象,只有过滤器也列出相同的数据类型,并也没有指定URI的情况下,才能通过检测。

D.包含了URI和数据类型的Intent对象(或者是数据类型能够从URI中推断出来)只有它的类型跟过滤器中列出的一个类型匹配,才能通过数据类型部分的检测,如果它的URI部分跟过滤器中的一个URI匹配或者Intent对象有一个content:或file:URI并且过滤器没有指定URI,那么才能能够URI部分的检测。换句话说,如果过滤器仅列出了数据类型,那么一个组件被假设为支持content:和file:数据。

如果一个Intent对象能够通过多个过滤器传递给一个Activity或Service,那么可以询问用户要激活哪个组件。如果没有找到目标,就会产生一个异常。

常见情况

以上数据检测规则中的最后一条(规则d),反映了组件能够获得从文件或内容提供器中获取本地数据的期望。因此,它们的过滤器能够只列出数据类型,并且不需要明确的命名content:和file:方案。这是一种典型的情况。例如,像下面的<data>元素那样,告诉Android,组件能够从一个内容提供器中获取图片并显示它:<data android:mimeType="image/*" />

因为大多数有效的数据是通过内容提供器来配发的,所以,指定一个数据类型但没有URI的过滤器或许是最常见的。

另一个常见的配置是带有方案和数据类型的过滤器。例如,像下面这样的<data>元素会告诉Android组件能够从网络上获取视频并显示它。

<data android:scheme="http" android:type="video/*" />

例如,我们来研究一下当用户点击一个网页上的一个链接时,浏览器应用程序要做的事情。首先,它会试着来显示数据(如果这个链接是一个HTML页,那么就显示)。如果不能显示这个数据,那么就会把方案和数据类型一起放到一个隐式Intent对象中,并试着启动一个能够做这项工作的Activity,如果没有接受者,它就会要求下载管理器来下载数据。然后把数据放在一个内容提供器的控制之下,以便一个潜在的更大的Activity池(那些只带有命名数据类型的过滤器)能够响应。

大多数应用程序都有不引用任何特殊数据就启动刷新的方法。能够初始启动应用程序的Activity都有带有“android.intent.action.MAIN”作为指定动作的过滤器。如果它们代表了在应用程序中的启动器,那么它们也要指定“android.intent.category.LAUNCHER”分类:

<intent-filter . . . >
    <action android:name="code android.intent.action.MAIN" />
    <category android:name="code android.intent.category.LAUNCHER" />
</intent-filter>

使用Intent对象进行匹配

Intent对象跟过滤器匹配不仅是要发现要激活的目标组件,而且也发现设备上有关组件集的一些事情。例如,Android系统通过查找所有的拥有指定的“android.intent.action.MAIN”动作和“android.intent.category.LAUNCHER”分类的过滤器的Activity,把它们填充到应用程序的启动器,把用户启动的有效的应用程序显示在屏幕的顶层,然后显示启动器中的那些Activity的图标和标签。类似地,系统通过查找它的过滤中带有“android.intent.category.HOME”的Activity来发现主屏界面。

应用程序能够用于Intent对象匹配的是组类似的方法。PackageManager类中有一组query…()方法,它们返回能够接受一个特殊Intent对象的所有组件,并且还有一组类似的resolve…()方法,用来判断响应一个Intent对象的最好组件。例如,queryIntentActivities()方法返回一个能够执行这个Intent对象要求动作的所有Activity;queryIntentServices()方法类似地返回Service列表。这些方法都不激活组件,它们只是列出能够响应这个Intent对象的所有组件。对于Broadcast Receiver也有类似的方法:queryBroadcastReceivers()。


原文地址:《Android Intents和Intent过滤器(二)》


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:73622次
    • 积分:1021
    • 等级:
    • 排名:千里之外
    • 原创:28篇
    • 转载:20篇
    • 译文:0篇
    • 评论:19条