三种应用程序基本组件——activity, service和broadcast receiver——是使用称为intent的消息来激活的。Intent消息传递是一种组件间运行时绑定的机制. intent是Intent对象, 它包含了需要做的操作的描述, 或者, 对于广播来说, 包含了正在通知的消息内容. 对于向这三种组件发送intent有不同的机制:
在上述三种情况下, android系统会自己找到合适的activity, service, 或者 broadcast receivers来响应intent. 三者的intent相互独立互不干扰.
一个intent对象包含了接受该intent的组件的信息(例如需要的动作和该动作需要的数据)和android系统所需要的信息(例如该组件的类别,以及如何启动它). 具体的说:
组件名称
组件名称是可选的. 如果设定了的话, Intent对象会被传给指定的类的一个实例. 如果不设定, 则android使用其它信息来定位合适的目标.
组件名称是使用setComponent(), setClass(),或 setClassName()来设定, 使用 getComponent()来获取.
Action常量 | 目标组件 |
动作 |
---|---|---|
ACTION_CALL | activity | 发起一个电话呼叫. |
ACTION_EDIT | activity | 显示数据给用户来编辑. |
ACTION_MAIN | activity | 将该activity作为一个task的第一个activity启动,不传入参数也不期望返回值. |
ACTION_SYNC | activity | 将设备上的数据和一个服务器同步. |
ACTION_BATTERY_LOW | broadcast receiver | 发出电量不足的警告. |
ACTION_HEADSET_PLUG | broadcast receiver | 一个耳机正被插入或者拔出. |
ACTION_SCREEN_ON | broadcast receiver | 屏幕被点亮. |
ACTION_TIMEZONE_CHANGED | broadcast receiver | 时区设置改变. |
你也可以定义自己的action字符串用来启动你的应用程序. 自定义的action应该包含应用程序的包名.例如"com.example.project.SHOW_COLOR".
action很大程度上决定了intent的另外部分的结构, 就像一个方法名决定了它接受的参数和返回值一样. 因此, 最好给action一个最能反映其作用的名字.
一个intent对象中的action是使用getAction()和setAction()来读写的.
Data
当将一个intent和一个组件相匹配时, 除了URI外数据类型也很重要. 例如, 一个显示图片的程序不应该用来处理声音文件.
数据类型常常可以从URI推断, 特别是content:URI, 它表示该数据属于一个content provider. 但数据类型也可以被intent对象显示声明. setData()方法设置URI, 而setType()方法指定MIME类型, setDataAndType()设置数据URI和MIME类型. 它们可以使用getData()和getType()来读取.
Category常量 | 含义 |
---|---|
CATEGORY_BROWSABLE | 目标activity可以使用浏览器来显示-例如图片或电子邮件消息. |
CATEGORY_GADGET | 该activity可以被包含在另外一个装载小工具的activity中. |
CATEGORY_HOME | 该activity显示主屏幕,也就是用户按下Home键看到的界面. |
CATEGORY_LAUNCHER | 该activity可以作为一个任务的第一个activity,并且列在应用程序启动器中. |
CATEGORY_PREFERENCE | 该activity是一个选项面板. |
addCategory()方法为一个intent对象增加一个category, removeCategory删除一个category, getCategories()获取intent所有的category.
Extras
intent对象有一系列put...()和set...()方法来设定和获取附加信息. 这些方法和Bundle对象很像. 事实上附加信息可以使用putExtras()和getExtras()作为Bundle来读和写.
Flags各种标志. 很多标志指示android系统如何启动一个activity(例如该activity属于哪个任务)和启动后如何处理它(例如, 它是否属于最近activity列表中).
android系统和应用程序使用intent对象来送出系统广播和激活系统定义的组件.
intent有两种:
Android将显式intent发送给指定的类. intent对象中名字唯一决定接受intent的对象.
对于隐式intent, android系统必须找到最合适的组件来处理它. 它比较intent的内容和intent filter. intent filter是组件的一个相关结构, 表示其接受intent的能力. android系统根据intent filter打开可以接受intent的组件. 如果一个组件没有intent filter, 那么它只能接受显式intent. 如果有, 则能同时接受二者.
当一个intent和intent filter比较时, 只考虑三个属性: action, data, category.
extra和flag在intent解析中没有用.
activity, service和broadcast receiver可以有多个intent filter来告知系统它们能接受什么样的隐式intent. intent filter的名字很形象: 它过滤掉不想接受的intent, 留下想接受的intent. 显式intent无视intent filter.
一个组件对能做的每件事有单独的filter. 例如, 记事本程序的NoteEditor activity有两个filter -- 一个启动并显示一个特定的记录给用户查看或编辑, 另一个启动一个空的记录给用户编辑.
一个intent filter不一定安全可靠. 一个应用程序可以让它的某个组件去接受隐式intent, 但是它没法防止这个组件接受显示intent. 其它的程序总是可以使用自定义的数据加上显式的程序名称来调用该组件.
一个intent filter是IntentFilter类的实例, 但是它一般不出现在代码中,而是出现在android Manifest文件中, 以<intent-filter>的形式. (有一个例外是broadcast receiver的intent filter是使用 Context.registerReceiver()来动态设定的, 其intent filter也是在代码中创建的.)
一个filter有action, data, category等字段. 一个隐式intent为了能被某个intent filter接受, 必须通过3个测试. 一个intent为了被某个组件接受, 则必须通过它所有的intent filter中的一个.
Action 测试
一个intent对象只能指定一个action, 而一个intent filter可以指定多个action. action列表不能为空, 否则它将组织所有的intent.
一个intent对象的action必须和intent filter中的某一个action匹配, 才能通过.
如果intent filter的action列表为空, 则不通过.
如果intent对象不指定action, 并且intent filter的action列表不为空, 则通过.
Category 测试
<intent-filter . . . >
<category android:name="android.intent.category.DEFAULT" />
注意前面说到的对于action和category的常数是在代码中用的,而不是manifest文件中用的. 例如,CATEGORY_BROWSABLE常数对应xml中的表示为"android.intent.category.BROWSABLE".
一个intent要通过category测试, 那么该intent对象中的每个category都必须和filter中的某一个匹配.
理论上来说, 一个intent对象如果没有指定category的话, 它应该能通过任意的category 测试. 有一个例外: android把所有的传给startActivity()的隐式intent看做至少有一个category: "android.intent.category.DEFAULT". 因此, 想要接受隐式intent的activity必须在intent filter中加入"android.intent.category.DEFAULT". ("android.intent.action.MAIN" 和"android.intent.category.LAUNCHER"的intent filter例外. 它们不需要"android.intent.category.DEFAULT".)
每个<data>元素指定了一个URI和一个数据类型. 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 authority. 如果host未指定,则port被忽略.
这些属性都是可选的,但它们并非相互独立: 要使一个authority有意义,必须指定一个scheme. 要使一个path有意义, 必须指定一个scheme和一个authority.
当intent对象中的URI和intent filter中相比较时, 它只和filter中定义了的部分比较. 例如, 如果filter中之定义了scheme,那么所有包含该scheme的URI的intent对象都通过测试.对于path来说,可以使用通配符来进行部分匹配.
<data>元素的type属性指定了数据类型. 它在filter中比在URI中更常见. intent对象和filter都可以使用"*"通配符作为子类型. 例如"text/*" "audio/*"表示所有的子类型都匹配.
data测试的规则如下:
如果一个intent可以通过多于一个activity或者service的filter, 那么用户可能会被询问需要启动哪一个. 如果一个都没有的话, 那么会抛出异常.
上述的最后一个规则(d)说明了组件通常可以从文件和content provider中获取数据. 因此, 它们的filter可以只列出数据类型不列scheme. 这是个特殊情况. 下列<data>元素告诉android该组件可以从一个content provider取得图像数据并显示之:
<data android:mimeType="image/*" />
由于大部分可用的数据由content provider提供, 指定数据类型但不指定uri的filter是最常见的情况.
另外一个常见的配置是filter具有一个scheme和一个数据类型. 例如, 下列<data>元素告诉android该component可以从网络获取图像数据并显示之:
<data android:scheme="http" android:type="video/*" />
考虑用户点击一个网页时浏览器的动作. 它首先试图显示这个数据(当做一个html页来处理). 如果无法显示, 则创建一个隐式intent, 并启动一个可以处理它的activity. 如果没有这样的activity, 那么它请求下载管理器来下载该数据. 然后它将数据置于一个content provider的控制之下, 这样有很多activity(拥有只有数据类型的filter)可以处理这些数据.
大部分应用程序还有一种方法来单独启动, 不需要引用任何特定的数据. 这些能启动应用程序的activity具有action为"android.intent.action.MAIN" 的filter. 如果它们需要在应用程序启动器中显示, 它们必须指定"android.intent.category.LAUNCHER" 的category.
<intent-filter . . . >
intent和intent filter相匹配, 不仅为了寻找并启动一个目标组件, 也是为了寻找设备上组件的信息. 例如, android系统启动了应用程序启动器, 该程序位于屏幕的顶层, 显示了用户可以启动的程序, 这是通过查找设备上所有的action为"android.intent.action.MAIN" ,category为"android.intent.category.LAUNCHER"的intent filter所在的activity实现的. 然后它显示了这些activity的图标和标题. 类似的, 它通过寻找 "android.intent.category.HOME"的filter来定位主屏幕程序.
应用程序可以用相同的方式来使用intent匹配. PackageManager 有一组query...()方法来寻找接受某个特定intent的所有组件, 还有一系列resolve...()方法来决定响应一个intent的最佳组件. 例如, queryIntentActivities()返回一个activity列表, 这些activity可以执行传入的intent. 类似的还有queryIntentServices()和queryIntentBroadcastReceivers().
记事本示例程序让用户可以浏览一个笔记列表, 查看, 编辑, 删除和增加笔记. 这一节关注该程序定义的intent filter.
在其manifest文件中, 记事本程序定义了三个activity, 每个有至少一个intent filter. 它还定义了一个content provider来管理笔记数据. manifest 文件如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
第一个activity, NoteList, 和其它activity不同, 因为它操作一个笔记的目录(笔记列表), 而不是一个单独的笔记. 它一般作为该程序的初始界面. 它可以做以下三件事:
该filter声明了记事本应用程序的主入口. 标准的MAIN action是一个不需要任何其它信息(例如数据等)的程序入口, LAUNCHER category表示该入口应该在应用程序启动器中列出.
该filter声明了改activity可以对一个笔记目录做的事情. 它允许用户查看或编辑该目录(使用VIEW和EDIT action), 或者选取特定的笔记(使用PICK action).
<data>元素的mimeType指定了这些action可以操作的数据类型. 它表明该activity可以从一个持有记事本数据的content provider(vnd.google.note)取得一个或多个数据项的Cursor(vnd.android.cursor.dir).
注意该filter提供了一个DEFAULT category. 这是因为 Context.startActivity() 和Activity.startActivityForResult()方法将所有的intent都作为作为包含了DEFAULT category来处理, 只有两个例外:
因此, 除了MAIN和LAUNCHER的filter之外, DEFAULT category是必须的.
这个filter描述了该activity能够在不需要知道目录的情况下返回用户选择的一个笔记的能力. GET_CONTENT action和PICK action相类似. 在这两者中, activity都返回用户选择的笔记的URI. (返回给调用startActivityForResult()来启动NoteList activity的activity.) 在这里, 调用者指定了用户选择的数据类型而不是数据的目录.
这个数据类型, vnd.android.cursor.item/vnd.google.note, 表示了该activity可以返回的数据类型 -- 一个笔记的URI. 从返回的URI, 调用者可以从持有笔记数据的content provider(vnd.google.note)得到一个项目(vnd.android.cursor.item)的Cursor.
也就是说, 对于PICK来说, 数据类型表示activity可以给用户显式的数据类型.对于GET_CONTENT filter, 它表示activity可以返回给调用者的数据类型.
下列intent可以被NoteList activity接受:
action: android.intent.action.MAIN
第二个activity, NoteEditor, 为用户显示一个笔记并允许他们编辑它. 它可以做以下两件事:
这个activity的主要目的是使用户编辑一个笔记--VIEW或者EDIT一个笔记. (在category中,EDIT_NOTE是EDIT的同义词.) intent包含匹配MIME类型vnd.android.cursor.item/vnd.google.note的URI--也就是某一个特定的笔记的URI. 它一般来说是NoteList activity中的PICK或者GET_CONTENT action返回的.
像以前一样,该filter列出了DEFAULT category.
该activity的第二个目的是使用户能够创建一个新的笔记, 并插入到已存在的笔记目录中. 该intent包含了匹配vnd.android.cursor.dir/vnd.google.note的URI, 也就是笔
有了这些能力, NoteEditor就可以接受以下intent:
action: android.intent.action.VIEW最后一个activity, TitleEditor, 允许用户编辑笔记的标题. 这可以通过直接调用activity(在intent中设置组件名称)的方式来实现. 但是这里我们用这个机会来展示如何在已有数据上进行另外的操作(类似于windows中的打开方式->程序列表 -- 译者注):
<intent-filter android:label="@string/resolve_title">
除了支持DEFAULT category之外,title编辑器还支持了另外两个category: ALTERNATIVE和SELECTED_ALTERNATIVE. 这些category标志着activity可以在选项菜单中呈现给用户(就像LAUNCHER category表示activity可以在程序启动器中一样). 注意filter还提供了一个显示标签(android:label="@string/resolve_title")来更好的控制用户在选项菜单中看到的内容.
有了这些能力, 以下的intent就可以被TitleEditor接受:
action: com.android.notepad.action.EDIT_TITLEhttp://blog.csdn.net/lmhit/article/details/5576250