应用程序的三种核心组件—activity,service和broadcast receiver---通过消息被激活,这些消息称为intent。Intent消息是用来在运行时对在相同或不同应程序中的两个组件进行绑定的对象。Intent对象本身是一个包含了对一个要执行的操作的抽象描述的被动(passive)数据结构。在不同的组件之间传递intent有不同的机制:
在每种情况,Android系统都会找到合适的activity,service或者broadcast receiverl来回应intent,在需要的时候实例化。这些信息系统没有重叠:Broadcast intent只会被传递给broadcast receiver,绝不会传递给activity或service。
一个Intent对象是一些信息的包裹。它包含了要接收该intent的组件感兴趣的信息(如要执行的动作以及需要的数据),还包含了Android系统感兴趣的附加信息(如可以接收该intent的组件的类别以及如何加载一个目标activity的指令)。Intent主要包含以下内容:
组件的名称
要接收这个intent的组件的名称。这个字段(field)是一个ComponentName对象—一个类完整的名字(如"com.example.project.app.FreneticActivity")和在应用程序manifest文件中设置的组件所属的包名(如"com.example.project")的联合。组件名是可选的。如果设置了组件名,Intent对象会传递给指定类的实例。如果没有设置组件名,Android用在intent中的其他信息来定位合适的目标。
常用setComponent(),setClass()或setClassName()来设置组件名称,用getComponent()来读取组件名。
Action
用一个字符串来命名被执行的action。Intent类定义个一些action常量,如下:
常量 |
目标组件 |
执行的动作(Action) |
ACTION_CALL |
activity |
初始化一个电话呼叫 |
ACTION_EDIT |
activity |
显示数据来让用户编辑 |
ACTION_MAIN |
activity |
开始一个tast的初始的activity,输入数据,也不输出返回 |
ACTION_SYNC |
activity |
将service中的数据与手机设备中的数据同步。 |
ACTION_BATTERY_LOW |
broadcast receiver |
警告电量过低 |
ACTION_HEADSET_PLUG |
broadcast receiver |
耳机被出入或拔出设备 |
ACTION_SCREEN_ON |
broadcast receiver |
屏幕关闭 |
ACTION_TIMEZONE_CHANGED |
broadcast receiver |
时区的设置改变 |
其他的action声明分布在Android API的他处。你也可以定义自己的action字符串,这时需要将应用程序包名作为你的action字符串的前缀。如"com.example.project.SHOW_COLOR"。Action字段很大程度上决定了intent其他的结构---特别是data字段和extras字段----如同函数名决定了形参和返回值。因此使action名字尽可能的明确并与intent的其它字段紧密联系是个好主意。
通过setAction()设置intent的action,并用getAction()读取。
Data
要处理的数据的URI或MIME类型。不同的action配有不同的数据规范。比如说,如果action字段是ACTION_EDIT,data字段应该包含要显示和编辑的文档的URI,如果action字段是ACTION_CALL,data字段应该是一个tel:含有要呼叫的号码的URI,如果action字段是ACTION_VIEW,data字段应该是一个http:接收activity将要下载和显示的数据的URI。
当处理一个intent时,知道其除URI之外的数据类型(它的MIME类型)往往十分重要。比如说,一个可以显示图片的组件不能用来播放音乐。
在很多情况下,数据的类型可以由其URI推断出-----特别是content:URIs, 它指明了数据在一个设备中并由content provider控制。但是类型也可以在Intent对象中明确的设置。setData()方法只指定了数据的URI,setType()方法指定数据的MIME类型,setDataAndType()方法设置数据了URI和MIME类型。可以用getData()获得数据,用getType()获得类型。
Category
可以接收这个intent的组件的类别的字符串。Intent对象中可以放置多个category字符串。Intent类定义了一些,如下:
Constant |
Meaning |
CATEGORY_BROWSABLE |
The target activity can be safely invoked by the browser to display data referenced by a link — for example, an image or an e-mail message. |
CATEGORY_GADGET |
The activity can be embedded inside of another activity that hosts gadgets. |
CATEGORY_HOME |
The activity displays the home screen, the first screen the user sees when the device is turned on or when the HOME key is pressed. |
CATEGORY_LAUNCHER |
The activity can be the initial activity of a task and is listed in the top-level application launcher. |
CATEGORY_PREFERENCE |
The target activity is a preference panel. |
使用addCategory()方法将一个category设置到intent对象,removeCategory()删除一个之前添加的category,getCategories()获得当前intent对象里的所有category。
Extras
要传递给目标组件的以键值对形式存在的附加信息。正如一些action与数据URI配对使用,action也要与extras相匹配。比如,一个ACTION_TIMEZONE_CHANGED intent有一个"time-zone"extra来指明新的时区,一个ACTION_HEADSET_PLUG 有一个"state" extra来指明耳机是否被插入或拔出,以及"name" extra来指明耳机的类型。如果你想自定义一个SHOW_COLOR action,那颜色的值将被设置成extra键值对。
Intent对象有一系列的如同putXXX()或getXXX()方法来设置或取得不同类型的extra数据。这些方法类似于Bundle对象。事实上,extra可以作为Bundle对象被加载或读取,使用putExtras()和getExtras()方法。
Flags
各种各样的Flags。许多指定了Android系统如何加载一个activity以及加载以后如何处理(如,是否属于最近的activity的清单)。所有的flag都在Intent类中定义。
Intent可以被分为两组:
Android在将explicit intent传递给指定目标组件的类的实例时,Intent对象中的component name 字段对确定那个组件应该接收该intent至关重要。
另一种不同的策略需要implicit intents。因为没有指定目标,Android系统必须找到最合适的组件来处理这个intent。---一个单独的activity或者service来执行请求的action或者broadcast receiver来回应boardcast通告。用Intent对象的内容与intent flilter(与组件相关的用来接收intent的结构)。Filter公布组件的能力以及限制可以处理的intent。它们可以接收公布的类型的implicit intents的组件开放。如果一个组件没有任何intent filter,它只可以接收explicit intent。一个有filter的组件既可以接收implicit intents也可接收explicit intent。
Intent只有3个字段在于intent flilter比对的时候起作用:action 、data ( URI 和 data type) 、category。extras 和 flags在解析哪个组件可以接收这个intent时不起作用。
Intent filter
为了通知系统何种implicit intents可以处理,activity,service和broadcast receiver可以有一个或者多个intent filter。每个intent filter描述了一个组件的能力以及它们愿意接收的intent集合。接收它们需要的intent,并过滤掉不希望接收的intent(只是implicit intents)。explicit intent必然会传递给它的目标,无论其是否包含intent filter。
一个组件对每项工作都有单独的fliter。
一个intent filter是一个IntentFilter类的实例。然而,因为Android系统在加载组件前必须知道这个组件的能力,所以intent filter常常不在java代码中建立,而是在应用程序的manifest文件(AndroidManifest.xml)中设置<intent-filter>元素。(一个例外是broadcast receiver的filter是调用Context.registerReceiver()来动态注册的,它们直接作为IntentFilter对象创建)。
一个filter有类似于Intent对象的action, data和category 的字段。一个被传递到该组件的implicit intent会被检测是否匹配这3个字段。一旦有一个不匹配Android系统就不会将该intent传递给组件。
3中测试的细节如下:
Action test
使用<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,但是一个filter对象却可以拥有多个action。一个filter必须包含至少一个<action>,否则它将阻断所有的intent。
为了通过这个测试,在Intent对象里的acyion必须与filter里的action列表中的一个匹配。如果intent对象或者filter未指定action,结果如下:
Category test
<intent-filter . . . > <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> . . . </intent-filter>
请注意前面章节介绍的action和category的常量不会用在manifest文件。取而代之的是一个完整的字符串值。比如说上面例子中的"android.intent.category.BROWSABLE
"对应 CATEGORY_BROWSABLE
常量。类似的,"android.intent.action.EDIT
"对应ACTION_EDIT
常量。
一个intent想通过测试,它的所有category必须与filter的匹配。Filter可以含有额外的category,但是不能遗漏任何在intent中的category。
原则上,一个没有category的intent总是能通过测试,不用考虑filter中有什么。然而有一个例外:如果implicit intents 有一个"android.intent.category.DEFAULT
" category( CATEGORY_DEFAULT
常量),Android会将它们传递给startActivity(),因此希望接收implicit intents的activity在filter中必须包含"android.intent.category.
DEFAULT
"。(拥有"android.intent.action.MAIN
" and "android.intent.category.
LAUNCHER
"的filter是一个例外,它们表示activity开始了一个新的task,并在程序加载界面显示了,它们可以包含一个"android.intent.category.DEFAULT
",但是却不需要这么做)。
Data test
<data>子标签,可以有多个或没有。
<intent-filter . . . > <data android:mimeType="video/mpeg" android:scheme="http" . . . /> <data android:mimeType="audio/mpeg" android:scheme="http" . . . /> . . . </intent-filter>
每个<data>元素可以指定URI和数据类型(MIME media type)。URI有一些单独的属性-- scheme
, host
, port
, 和path
。
scheme://host:port/path
举例说,下面的URI:
content://com.example.project:200/folder/subfolder/etc
以上所述的URI的各部分是:scheme是"content
",host是"com.example.project
",port是"200
",path是"folder/subfolder/etc
"。
host和port共同组成了URI authority(权限)。如果host未指定,则port将被忽略。
每个属性都是可选的,但是它们并不独立于其他属性:为了authority有意义必须指定scheme,为了path有意义必须指定scheme和authority。
当Intent对象的URI与filter中的URI对比时,只会比较filter中的URI的一部分。比如说,如果一个filter只指定了scheme,所有含有该scheme的URI都匹配这个filter。如果一个filter只指定了scheme和authority但是没有path,所有含有该scheme和authority的URI都匹配这个filter,不用考虑path。如果filter指定了scheme, authority和path,只有Intent的URI的scheme, authority和path都匹配时才可通过。然而,一个path中可以包含通配符来允许部分路径的匹配。
<data>元素的tyoe属性指明了数据的MIME类型。在filter中要比URI更普遍。Intent对象和filter都可以在子类型字段中使用”*”通配符---比如说"text/*"或"audio/*"--------标识所有的子类型匹配。
Data测试会比较Intent对象和filter的URI和数据类型。规则如下:
A. 如果一个Intent对象既不包含URI也不包含数据类型,只有在filter既没有指定URI没有指定数据类型时才可通过测试。
B. 如果一个Intent对象包含URI但不包含数据类型(且数据类型不能从URI推断出),只有在匹配了filter指定的URI并且filter没有指定数据类型时才可通过测试。这些情况只适合像mailto:
和tel:
这样的没有指向实际数据的URI
C. 如果一个Intent对象不包含URI但是包含数据类型,只有filter有相同的数据类型且没有指定URI才可通过
D. 如果一个Intent对象既包含URI又包含数据类型(或者数据类型可以由URI推断出),要通过测试必须:数据类型可以匹配,URI匹配或者filter支持 content: 或 file: 的URI且filter未指定URI。换句话说,当一个filter只有数据类型时,它被假定支持 content: 和 file:数据。
如果一个intent可以通过不止一个activity或者service,系统会询问允许选择哪个组件来处理。如果没有匹配时一个exception会抛出。
一般的情况:
上面最后一条显示的是测试和规则,反映了组件可以从文件或content provider获取局部(local)数据。因此,一个filter可以只是指定数据类型而不需要明确的命名 content:
和 file:
scheme。这是一个典型的情况。比如说,如下的<data>元素告诉系统这个组件可以从content provider接收图像数据并显示它。
<data android:mimeType="image/*" />
因为多数可用的数据由content provider分配,指定了数据类型而不指定URI的filter或许会最为普遍。
另一种普遍的设置是指定了scheme 和数据类型的filter。比如说一个如下的<data>元素告诉系统这个组件可以从互联网获得vedio并播放它。
<data android:scheme="http" android:type="video/*" />
考虑下:当用户点击一个网页中的链接时,浏览器程序会怎么做。它首先会试图显示数据,如果不能显示数据,它会收集一个指定了scheme 和数据类型的implicit intent,并试图启动一个可以做这项工作的activity。如果没有响应者,
它将要求下载管理器下载数据。这将有一个content provider来控制它,所以一个潜在的大量的activity(那些含有只指明了数据类型的filter的activity)可以回应。
大多数应用程序可以不涉及任何特殊数据的情况下重启。可以作为启动应用程序的activity会指定其action为"android.intent.action.MAIN
".如果它们在程序加载器中表示,它们会指定"android.intent.
category.LAUNCHER
"category。
<intent-filter . . . > <action android:name="code android.intent.action.MAIN" /> <category android:name="code android.intent.category.LAUNCHER" /> </intent-filter>
使用intent匹配:
将Intent与Intent filter匹配不仅用于发现目标组件来处理该intent,还用来发现在设备上的组件的一些信息。比
如说,Android系统为了确定哪些应用程序可以罗列在程序加载器或桌面上并可以让用户加载,它会搜索所有带有"android.intent.action.MAIN
"action和"android.intent.category.LAUNCHER
"category的intent filter 的activity。然后将这些activity的图标添加到应用程序加载器。类似的,为了发现主屏幕,它会寻找带有"android.intent.category.HOME
"filter的activity。
你的应用程序可以以相似的方式利用intent加载。PackageManager有一些queryXXX()方法来返回所有可以接收特定intent的组件。比如说, queryIntentActivities()
方法返回所有可以执行作为参数传递的intent的activity。queryIntentServices()
返回类似的service。