应用程序核心组件中的三个Activity,service,还有broadcast receiver都是通过一个叫做intent的消息激活的。Intent消息传送是在相同或不同的应用程序中的组件之间后运行时绑定的一个设施。Intent对象也就是它自己是一个数据结构,这个数据结构持有将要执行操作的抽象描述,或者在broadcast的情况下,是一个已经发生而将要宣布的描述。为传递intent到每个不同类型的组件有单独的机制:
- 一个Intent对象被传递到Context.startActivity()或是Activity.startActivityForResult()方法来启动一个Activity或是得到一个已经存在的Activity来做一些新的事情。(也可以被传递到Activity.setResult()来返回到一个叫做startActivityForResult()的Activity。)
- 一个Intent对象被传递到Context.startService()来初始化一个service或是传递一个新的指令到一个已经运行的service。同样的,一个Intent也可以被传递到Context.bindService()来建立调用组件与目标服务之间的连接。还可以随意的初始化一个没有运行的service。
- 传递给broadcast的方法中的任意一个方法(例如Context.sendBroadcast(),Context.sendOrderedBroadcast或是Context.sendStickyBroadcast())的Intent对象被传送给所有感兴趣的广播接收者。
在那样的情况下,Android系统将找到适当的Activity,service或是broadcast receiver集合来响应这个Intent,如果有必要的话还会实例化它们。那些消息系统内没有重叠部分:broadcast 的Intent只对应broadcast receiver,而不会给Activity或是service。一个传递给startActivity()方法的Intent只会被传递给一个Activity,绝不会传递给一个service或是broadcast receiver,等等。
这份文档开始于Intent对象的描述。然后会描述Android映射Intent到组件使用的法则,讲述了哪一个组件应当接受一个Intent消息。对于Intent来说并没有准确的指明一个目标组件,进程将潜心于检测Intent与关联了潜在的目标组件的Intent filters。
Intent Objects | Intent对象
一个Intent对象是一个信息束。它容纳了接受Intent组件感兴趣的信息(例如将要执行的动作还有将要对其起作用的数据)加上Android系统感兴趣的信息(比如应当处理这个Intent的组件的种类还有如何启动目标Activity的指令)。主要地它能包含以下几点:
Component name | 组件名
应当处理Intent组件的名字。这个字段是一个ComponentName对象,所有有资格的目标组件类名(例如”com.example.project.app.FreneticActivity”)集合还有应用程序manifest文件中应用程序所在的包名(例如”com.example.project”)集合。组件名和包名集合中的包在manifest中不是必须一定要匹配的。
组件名是可选的。如果被设置了,Intent对象会被传送给指定类的实例。如果没有设置组件名,Android系统会使用Intent对象中的其他信息来定位一个合适的目标,在后文中请查看关于Intent Resolution.
组件名可以通过setComponent(),setClass,或是setClassName方法来设置,也可以通过getComponent()方法来读取。
Action |动作
一个字符串命名了将要执行的动作—或者是在broadcast Intent的情况下,已经发生将要报告的动作。
Intent类定义了一系列的动作常量,包括这些:
Constant 常量 |
Target component 目标组件 |
Action 动作 |
ACTION_CALL |
activity |
Initiate a phone call. 初始化一个打电话界面 |
ACTION_EDIT |
activity |
Display data for the user to edit. 为用户展示数据来编辑 |
ACTION_MAIN |
activity |
Start up as the initial activity of a task, with no data input and no returned output. 作为任务的初始化Activity启动,没有数据输入也没有返回值输出。 |
ACTION_SYNC |
activity |
Synchronize data on a server with data on the mobile device. |
ACTION_BATTERY_LOW |
broadcast receiver |
A warning that the battery is low. 提示电量过低。 |
ACTION_HEADSET_PLUG |
broadcast receiver |
A headset has been plugged into the device, or unplugged from it. 耳机已经被插入设备或是从设备上拔出。 |
ACTION_SCREEN_ON |
broadcast receiver |
The screen has been turned on. 屏幕被转向。 |
ACTION_TIMEZONE_CHANGED |
broadcast receiver |
The setting for the time zone has changed. 时区设置被改变。 |
查看预定义的类内部动作常量列表请看Intent的类描述。其他的action被定义在Android API的其他地方。你也可以在应用程序中为激活组件定义你自己的动作字符串。你创造的action 常量应当包括包名作为前缀-例如:”com.example.project.SHOW_COLOR”。
动作主要的决定了Intent的其他部分是如何构造的,尤其是”data”还有”extras”字段,作为方法名决定参数列表还有返回值。因为这样,使用尽可能特殊的action名还有将他们与Intent中的其他字段名结合的紧一点是一个好主意。换句话来说,为Intent对象定义一整套协议而不是定义一个孤立action能够使你的组件更易于操作。
在Intent对象中的动作被”setAction()”方法设置,也能通过”getAction()”方法来获取。
Data | 数据
被执行数据的URI还有数据的MIME类型。不同的动作会被不同种类的数据规格来组成一对。例如,如果动作字段是ACTION_EDIT,数据字段会包含将要被展示编辑文档的URI。如果动作是ACTION_CALL,数据字段将会是包含tel:URI,将要拨打的电话。同样的,如果动作是ACTION_VIEW而数据字段是一个http:URI,那么接收的Activity将会被调用来下载或是展示URI表示的数据。
当匹配一个Intent到能胜任处理数据的组件的时候,除了URI之外知道数据的类型(MIME type)也是很重要的。例如,展示图像数据的组件不能被用作播放媒体文件调用。
在很多情况下,数据类型能通过URI推断出来尤其是content:URIs,表明了数据是在设备上存放的,被content provider来控制(请查看content provider的分开讨论)。但是类型也可以在Intent对象中准确的设置。setData()方法仅仅指定数据的URI,setType()方法仅仅指定数据的MIME类型,而setDataAndType()方法可以同时指定数据的URI和MIME类型。URI可以通过getData()方法获取,类型可以通过getType()获取。
Category | 种类
包含关于组件种类的额外信息的字符串可以处理Intent。许多种类描述可以放在Intent对象中。就像action一样,Intent类定义了许多category常量,包括以下这些:
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 目标Activity可以被浏览器安全的调用,通过一个链接来展示数据。例如一张图片或是一条邮件消息。 |
CATEGORY_GADGET |
The activity can be embedded inside of another activity that hosts gadgets. Activity可以被嵌入到另一个主机产品的Activity中。 |
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. 这个Activity展示了主屏幕,设备被翻转或是HOME键被按下的时候用户看见的第一个界面。 |
CATEGORY_LAUNCHER |
The activity can be the initial activity of a task and is listed in the top-level application launcher. 这个Activity可以是一个任务的启动Activity,在顶级应用程序启动器中被列出。 |
CATEGORY_PREFERENCE |
The target activity is a preference panel. 目标Activity是一个参数面板。 |
为全部的分类列表,请查看Intent类描述。
addCategory()方法在Intent对象中任命一个种类,removeCategory()方法来删除之前添加过的种类,getCategories()方法来获取当前对象中的所有种类集合。
Extras | 额外部分
额外信息的键值对,额外信息应当被分发到处理Intent的组件。正像一些action被特殊种类的URI配对,一些action也通过特殊的额外部分配对。例如,ACTION_TIMEZONE_CHANGED Intent有一个”time-zone”的额外部分,它定义了新的时区,ACTION_HADSET_PLUG有一个”state”额外部分表明了耳机现在是否是插入还是拔出的状态,也有一个”name”额外部分给耳机的类型。如果你要创造一个SHOW_COLOR的action,颜色值将会在一个额外键值对中设置。
Intent对象中有一系列的put...()方法来插入各种各样的额外数据的类型还有类似的get...()方法集合来读取这些数据。这些方法与Bundle对象中的方法是等价的。实际上,额外部分也可以被安放和读取作为Bundle通过使用putExtras()方法和getExtras()方法。
Flags | 标记
不同种类的标记。很多标记介绍了Android系统如何启动一个Activity(例如,Activity属于哪一个任务)还有在启动之后如何对待它(例如,它是否属于最近启动的Activity列表)。所有的标记都定义在Intent类中。
Android系统和伴随着平台的应用程序都使用Intent对象来发送系统原生广播还有激活系统组件。查看如何构造Intent来激活系统组件,请在参考文献中查阅list of intents.
Intent Resolution | Intent 解析
Intent 可以被分为两大类:
- 显式的Intent通过组件的名字指定了目标组件(之前提到过的component name字段有值集合)。既然组件名一般来说不能被其他应用程序的开发者所知道,准确的Intent是为全局应用程序消息所使用的,例如一个Activity启动一个从属的service或是启动一个同类型的Activity。
- 隐式的Intent并不命名目标(组件名字段为空)。隐式的Intent经常用于激活其他应用程序中的组件。
Android传递显式的Intent给一个指定的目标类的实例。Intent对象中除了组件名没有其他东西关心决定哪个组件应该获取到该Intent。
隐式的Intent需要不同的策略。在目标组件缺少的情况下,Android系统必须找到最好的组件(或是多个组件)来处理该Intent—单个的Activity或是service来执行请求的动作或是响应广播通知的广播接受者集合。它通过比较Intent对象的内容和Intent filter(一个关联了潜在接收Intent的组件的结构)来做到。过滤器通知组件的能力还有划定Intent可以处理的界限。他们打开这样一个组件,这个组件有可能接收已经通知了类型的隐式Intent。如果一个组件没有Intent filters,那么它只接受显示的Intent。一个有过滤器的组件既可以接收显式的也可以接收隐式的Intent。
一个Intent对象中只有三个方面被查阅,当Intent对象是针对于Intent filter测试的时候。Action、Data(both URI and data type)、Category。
Extra还有flag在解决组件接收Intent的时候并不起作用。
Intent filters
为了通知系统他们能处理哪一个隐式的Intent,Activity,service,broadcast receiver可以有一个多更多的Intent filter。每一个过滤器描述了组件的一个能力,一个罪案将会接收Intent的Intent集合。,实际上,Intent filter在希望类型的Intent中过滤,当过滤掉不想要的Intent时,但只有不想要的隐式Intent(那些没有指名一个目标类的Intent)。显示Intent总是被传递到他的目标,不管它是否包含Intent filter;过滤器没有被请教。但是隐式Intent传递给一个组件,只有Intent能传递给组件的过滤器之一的组件。
一个组件中每一个能做的工作有单独的过滤器,每一个能呈现给用户的外观。例如,记事本应用程序中的NoteEditor Activity有两个过滤器:一个用来启动用户可以查看或编辑的特定便签,另一个来开始一个新的空白便签,那么用户可以书写和保存。(所有的Note Pad的过滤器将在之后的Note Pad Example部分描述。)
Intent filter是IntentFilter类的实例。然而,既然Android系统必须在启动一个组件之前就得必须了解组件的能力,那么Intent filter一般来说不在Java代码中设置,而是在应用程序的manifest文件(AndroidManifest.xml)中作为<intent-filter>元素来定义。(有一个例外,通过调用Context.registerReceiver()方法动态注册的广播接收者,他们是作为intentFilter对象直接创建的)
过滤器有与action,data,category字段平行的Intent对象字段。隐式Intent在所有三个方面对过滤器进行测试。为了传递给拥有过滤器的组件,它必须通过所有的三项测试。如果即使没有通过他们中的一个,Android系统都不会传递到组件,至少不在那个过滤器的基础上。然而,即使一个组件有多个Intent filter,没有通过组件过滤器的Intent也可能在另一个上通过。
三项测试将在下面被详细的描述:
Action test | 动作测试
在manifest文件中的<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,过滤器也需要列出多于一个的action。Action列表不能为空,过滤器列表必须包含至少一个<action>标签否则的话它将阻塞所有的Intent。
为了通过这个测试,在Intent对象中指定的action必须与过滤器列表中的某一个action匹配。如果Intent对象或是过滤器没有指定一个action,结果就是以下列出的:
- 如果过滤器没有列出action,Intent将不会匹配到任何东西,所以所有的Intent都不能通过测试。没有Intent可以通过该过滤器。
- 另一方面,没有指定action的Intent对象将自动的通过测试,只要过滤器包含至少一个action。
Category test | 种类测试
<intent-filter>元素也列出了category作为子元素。例如:
<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测试,在Intent对象中的每一个种类必须匹配过滤器中的category。过利器可以列出附加的category,但是不能遗漏Intent中的任意一个category。
因此,原则上一个没有种类的Intent对象总是能通过category测试,不管过滤器中有什么。大部分情况下那是对的。然而有一个例外,Android让所有的隐式Intent通过startActivity()方法,好像他们包含至少一个种类:”android.intent.category.DEFAULT”(CATEGORY_DEFAULT常量)。因此,将要接收隐式Intent的Activity必须在他们的Intent过滤器中包含”android.intent.category.DEFAULT”。(有”android.intent.action.MAIN”还有”android.intent.category.LANCHER”设置的过滤器除外,他们标记了一个开始新任务的Activity然后在启动器中呈现出来。他们可以在category列表中包含”android.intent.category.DEFAULT”,但是不是必须的。)查看更多的那些过滤器的信息可以参见之后提到的Using intent matching。
Data test | 数据测试
就像action还有category一样,一个Intent filter的数据规格也是在一个子元素中包含的。在那些情况下这个子元素可以出现多次或是不出现。例如:
<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,还有path——每一个URI的不同部分: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就会被忽视。
那些属性中每一个都是可选的,但他们彼此之间互不依赖。authority是有意义的,scheme必须被指定。为了一个地址是有意义的,scheme和authority都必须被指定。
当Intent对象中的URI被与filter中的URI详述比较时,实际上只会与filter中提到的URI部分比较。例如,如果一个过滤器只指定了scheme,所有拥有该scheme的URI将会匹配这个过滤器。如果一个过滤器指定了scheme还有authority但是没有指定path,所有有相同scheme还有authority的URI能匹配,不管他们的path。如果一个过滤器指定了scheme,authority,还有path,只有有相同scheme,authority还有path的可以匹配。然而,在过滤器中path详细会包含通配符,只需要path的局部匹配。
<data>元素的”type”属性指定了数据的MIME类型。在过滤器中它比URI更常见。Intent对象还有过滤器都可以用”*”作为子类型的万能类型,例如”text/*”或是”audio/*”表明任意子类型的匹配。
数据测试比较URI还有Intent对象中的数据类型和过滤器中的URI还有数据类型。过则如下:
- 既没有URI也没有数据类型的Intent对象只有在过滤器没有指明URI还有数据类型的情况下才能通过测试。
- 有URI但是没有数据类型的(从URI中也不能推断出类型)Intent对象只有它的URI匹配了过滤器的URI,这个过滤器并没有指明一个类型。这将只会是这样的情况,像”mailto:”还有”tel:”一样的URI,它们并不涉及到实际的数据。
- 有数据类型但是没有URI的Intent对象只有在过利器列出了同样的数据类型同样的也没有指明URI的情况下能通过测试。
- 既有URI也有数据类型(或是从URI中可用推断出数据类型)的Intent对象,只有在它的类型匹配了过滤器中的一个类型才能通过测试的数据类型部分。如果它的URI匹配了过滤器中的一个URI或者它有”content:”或是”file:”URI并且过滤器没有指明URI的情况才才能公国测试的URI部分。换句话来说,组件是默认支持”content:”还有”file”数据的如果过滤器值列出了数据类型。
如果Intent可以通过多于一个的Activity或是service的过滤器,那么用户将会被询问激活哪一个组件。如果没有目标组件被找到那么会引发一个异常。
Common cases | 通常情况
上面提到最后一条数据测试的规则,反应了组件能够从文件或是content provider中获取本地数据的期望。因此,它们的过滤器只需要列出数据类型而不必要准确的命名”content:”和”file:”scheme.这是一个经典的案例。比如一个<data>元素像下面说的这样告诉Android组件可以从content provider中获取图像数据并且展示它:
<data android:mimeType="image/*" />
既然大部分的有效数据都是被content provider所提供,那么指明了数据类型但是没有URI的过滤器或许是最常见的。
另一个常见的配置是有scheme还有数据类型的过滤器。例如像下面的<data>元素告诉Android塑件可以从网络中获取视频数据并且展示它:
<data android:scheme="http" android:type="video/*" />
例如考虑当用户跟随了一个网页上的链接,浏览器程序做什么呢。它开始尝试展示数据(如果链接是一个HTML页面)。如果不能展示该数据,它将含有这个scheme的隐式Intent还有数据类型放在一起然后尝试打开一个能做这个任务的Activity。如果没有接收者,那么它请求下载管理器来下载该数据。那将把它放在content provider的管理下,那么有大量的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>
Using intent matching | 使用Intent匹配
Intent匹配Intent过滤器不仅是为了发现目标组件来激活,而且发现一些设备上的组集合的一些东西。比如,Android系统居住于应用程序启动器中,为用户来展示可用的应用程序并启动的顶级视图,通过找到所有定义了”android.intent.action.MAIN”action还有”android.intent.category.LAUNCHER”category过滤器的Activity。然后在启动器中展示那些Activity的图标还有标签,通过查找在过滤器中带有”android.intent.category.HOME”的Activity来展示home屏幕。
你应用程序使用Intent匹配也是类似的方式。包管理器(PackageManager)有一系列的query...()方法,它返回你可以接收特定Intent的所有组件,还有类似的一系列的resolve...()方法来决定响应Intent的最好组件。例如queryIntentActivities()返回能执行作为参数传过来Intent的所有Activity列表,queryIntentServices()返回一个类似的服务列表。这些方法并不激活组件,他们仅仅列出了能响应Intent的组件列表。对于broadcast receiver来说也有类似的方法,queryBroadcastReceivers().
Note Pad Example | 记事本例子
记事本示例程序可以让用户浏览便签列表,可以查看列表中的单独项的详细信息,编辑项,添加新项到列表中。这个章节将查看这个程序的manifest文件中的Intent filter。(如果你在SDK离线情况下工作,你可以查看到这个示例程序的所有源码,包括manifest文件在<sdk>/sample/NotePad/index.html中。如果你是在线浏览这份文档,源文件可以在Tutorials and Sample Code 章节中看到)
在这份manifest文件中,记事本应用程序定义了三个Activity,每一个都拥有至少一个Intent filter。也定义了一个管理Note数据的content provider。这是整个的manifest文件:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.notepad"> <application android:icon="@drawable/app_notes" android:label="@string/app_name" > <provider android:name="NotePadProvider" android:authorities="com.google.provider.NotePad" /> <activity android:name="NotesList" android:label="@string/title_notes_list"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.EDIT" /> <action android:name="android.intent.action.PICK" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.GET_CONTENT" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> </intent-filter> </activity> <activity android:name="NoteEditor" android:theme="@android:style/Theme.Light" android:label="@string/title_note" > <intent-filter android:label="@string/resolve_edit"> <action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.EDIT" /> <action android:name="com.android.notepad.action.EDIT_NOTE" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.INSERT" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" /> </intent-filter> </activity> <activity android:name="TitleEditor" android:label="@string/title_edit_title" android:theme="@android:style/Theme.Dialog"> <intent-filter android:label="@string/resolve_title"> <action android:name="com.android.notepad.action.EDIT_TITLE" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.ALTERNATIVE" /> <category android:name="android.intent.category.SELECTED_ALTERNATIVE" /> <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> </intent-filter> </activity> </application> </manifest>
第一个Activity NotesList 与其他Activity区分开来实际上是它是在一个Note的文件夹(note列表)上操作而不是单个Note。它一般作为进入应用程序的初始化用户界面。它通过三个Intent过滤器的描述能做三件事情:
<intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter>
这个过滤器定义了进入记事本应用程序的主入口。标准的”MAIN” action是一个入口,不需要Intent中的任何其他信息(例如没有数据说明),然后”LAUNCHER”category表明入口点会在应用程序启动器中列出来。
<intent-filter> <action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.EDIT" /> <action android:name="android.intent.action.PICK" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" /> </intent-filter>
这个过滤器声明了这个Activity能在一个Note文件夹做的事情。它可以允许用户查看或是编辑文件夹(通过”VIEW”还有”EDIT”动作),或是从文件夹中选择一个特别的Note(通过”PICK”动作)。
<data>元素的”mimeType”属性指明了action操作的数据类型。它表明了Activity可以从持有Note pad数据(vnd.google.note)的content provider中获取到一个大于零的游标或是更多的项目(vnd.android.cursor.dir)。启动Activity的Intent对象可以包含一个内容:URI指明了Activity应该打开的这种类型的额外数据。
在Note中”DEFAULT”category在过滤器中也被提供了。它在这是因为Context.startActivity还有Activity.startActivityForResult()方法视所有的Intent都有”DEFAULT”category,但是有两个除外:
- 显示指出了目标Activity的Intent。
- 包含”MAIN”action还有”LAUNCHER”category的Intent。
因此,”DEFAULT”category对于所有的过滤器都是需要的,除了那些有”MAIN”action还有”LAUNCHER”category的过滤器。(intent filter 并不为显示Intent咨询)
<intent-filter> <action android:name="android.intent.action.GET_CONTENT" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> </intent-filter>
这个过滤器描述了该Activity的能力来返回一个用户选择的Note,而用户没有要求文件夹中应当选择的任意的详述。”GET_CONTENT”动作类似”PICK”动作。两种情况下,Activity都从用户选择的Note中返回一个URI。(在那种情况下,URI被返回给Activity通过一个叫做startActivityForResult()来启动便签列表activity。)这里,然而调用者指定了希望的数据类型而不是用户将要选择的数据文件夹。
数据类型,”vnd.android.cursor.item/vnd.google.note”,表明了该Activity可以返回的数据类型,单个Note的URI。从返回的URI中,调用者可以从持有Note pad数据(vnd.google.note)的content provider中得到一个准确项(vnd.android.cursor.item)的游标。
换句话说,在之前的filter中”PICK”action,数据类型表明了Activity能够展示给用户的数据的类型。在”GET_CONTENT”过滤器中,它表明了Activity也可以返回数据的类型给调用者。
考虑到这些功能,下面的Intent将解决在NotesList activity:
Action:android.intent.action.MAIN 启动没有指明数据的Activity Action:android.intent.action.MAIN Category:android.intent.category.MAIN 启动没有选择数据指明的Activity。这是被顶级列表中的启动器所使用的实际Intent。所有匹配了这个action还有category的filter的Activity都会被添加到启动器。 Action:android.intent.action.VIEW Data:content://com.google.provider.NotePad/notes 请求Activity展示”content://com.google.provider.NotePad/notes”下的所有note的列表。用户可以浏览note列表也可以从中获取到项的信息。 Action:android.intent.PICK Data:content://com.google.provider.NotePad/notes 请求展示在”content://com.google.provider.NotePad/notes”下的note列表。用户可以从列表中选择一项然后Activity会返回该项的URI给启动NoteList Activity的Activity。 Action:android.intent.action.GET_CONTENT Data type:vnd.android.cursor.item/vnd.google.note 请求Activity提供Note pad数据中的一项数据。
第二项Activity,NoteEditor,展示用户一个Note实体而且允许用户编辑。它做两件事就像在他的两个Intent filter中描述的那样:
<intent-filter android:label="@string/resolve_edit"> <action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.EDIT" /> <action android:name="com.android.notepad.action.EDIT_NOTE" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> </intent-filter>
首先Activity主要的目的是让用户在单个Note上查看或是编辑它。(“EDIT_NOTE category”对于”EDIT”来说是同义的。)该Intent包含匹配了数据的URI,该URI匹配了MIME类型”vnd.android.cursor.item/vnd.google.note”,那就是说这个URI是一个特定note的URI。它会是被NoteList activity中”PICK”或是”GET_CONTENT”动作返回的URI。
就像之前说的,过滤器列出”DEFAULT”种类以便于该Activity可以被没有明确指出NoteEditor类的Intent启动。
<intent-filter> <action android:name="android.intent.action.INSERT" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" /> </intent-filter>
该Activity的第二个目的是确保用户可以创建一个新的便签,新的便签将会插入到已有的便签文件夹中。该Intent将会包含匹配了”vnd.android.cursor.dir/vnd.google.note”MIME类型的URI,也就是说,便签将会被放入的文件夹的URI。
考虑到这些功能,Intent将解决NoteEditor的Activity如下:
action: android.intent.action.VIEW data: content://com.google.provider.NotePad/notes/ID 请求Activity展示被id定义的note的内容。(关于content:URI 如何指定集合中单个的成员,请查看 content provider) action: android.intent.action.EDIT data: content://com.google.provider.NotePad/notes/ID 请求Activity展示没有被id定义的note,并且让用户编辑它。如何用户保存了更改,该Activity将会在content provider中更新note的数据。 action: android.intent.action.INSERT data: content://com.google.provider.NotePad/notes 请求Activity创建一个新的空白便签在”content://com.google.provider.NotePad/notes”便签列表中,而且允许用户编辑它。如果用户保存了便签,那么便签的URI将会返回给调用者。
最后一个Activity,TitleEditor,确保用户可以编辑便签的标题。在没有用Intent filter的情况下通过直接调用Activity(通过在Intent中显示的设置它的组件名)的方式也能被执行。但是这里我们抓住机会来展示如何在已经存在的数据上发布可选择的操作:
<intent-filter android:label="@string/resolve_title"> <action android:name="com.android.notepad.action.EDIT_TITLE" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.ALTERNATIVE" /> <category android:name="android.intent.category.SELECTED_ALTERNATIVE" /> <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> </intent-filter>
该Activity只有一个Intent filter用了一个叫做”com..android.notepad.action.DEIT_TITLE”的用户action.它必须在特定的note(数据类型为”vnd.android.cursor.item/vnd.google.note”)上被调用,就像之前的”VIEW”还有”EDIT”动作一样。然而这里,该Activity展示了note数据中的标题,而不是note的内容本身。
此外提供了通常的”DEFAULT”category,标题的编辑者也提供了两个其他的categories:”ALTERNATIVE”还有”SELECTED_ALTERNATIVE”。这些categories指出Activity可以在选项菜单中呈现给用户(就像”LAUNCHER”category确定了应用程序启动器中呈现给用户的Activity)。注意到过滤器也提供了一个明确的标签(通过android:label=”@string/resolve_title”)以更好的控制用户看到的东西,当呈现了这个Activity作为一个对他们正在查看的数据可选的动作。(更多关于那些categories还有创建选项菜单,请查看PackageManager.queryIntentActivityOptions()还有Menu.addIntentOptions()方法)
考虑到这些功能,接下来的Intent将在TitleEditor Activity中解决:
action: com.android.notepad.action.EDIT_TITLE data: content://com.google.provider.NotePad/notes/ID 请求Activity展示关联note ID的标题,并且允许用户编辑这个标题。
Android Google官方文档(cn)解析之——Intents and Intent filter