Intent和Intent过滤器:
英文原文:http://developer.android.com/guide/topics/intents/intents-filters.html
版本:Android 4.0 r1
译者署名:Rongqi Fan
译者链接:
程序的3个核心组件——Activity、services、广播接收器——是通过intent传递消息的。intent消息对于运行时绑定不同的组件是很方便的,这些组件可以是同一个程序也可以是不同的。一个intent对象,是一个被动的数据结构,它保存了一个操作的抽象描述——或通常是一个广播的实例,一些发生的事情的描述,一个通知。传递intent到不同组件的机制是互不相同的。
- intent对象是传递给Context.startActivity() 或Activity.startActivityForResult() 以启动Activity或是让一个存在的Activity做些事情。(也可以传递给Activity.setResult()来返回Activity的信息,这个函数叫startActivityForResult()。)
- intent对象传递给函数来初始化一个service或是分发一个新的指令给一个正在进行的service。同样,intent传递给来建立一个在调用组件和目标service间的联系。如果一个service没有运行,它可以开始它。
- intent可以传递给任何广播函数(如:Context.sendBroadcast()、Context.sendOrderedBroadcast()、 Context.sendStickyBroadcast()),intent被分派给所有感兴趣的广播接收者。很多广播源在系统内核里。
Android系统会寻找合适的Activity、service或设置广播接收器来响应intent,在需要的时候实例化它们。在消息系统里没有交叠:广播intent仅仅分派给广播接收器,不会分派给Activity或service。一个intent分派给startActivity()仅仅分派给Activity,不会分派给service或广播接收器,等等。
本文档先描述intent对象。然后描述Activity把intent映射到组件的规则——如何解决什么组件如何接收一个intent消息。没有显示指明目标组件的intent,进程通过intent过滤器测试intent对象来决定潜在的接收者。
intent对象
intent对象是一个信息桶。它包含了接收它的组件感兴趣的信息(如:携带的动作和数据),附加Android系统感兴趣的信息(如:处理intent和启动目标Activity指令的组件的类别)。主要包含如下信息:
组件名
处理intent的组件的名字。这个域是一个对象——包含了目标组件的全名(如:)包含在manifest文件里设置的组件驻留的包名(如:)。组件名的包的名不需要和在manifest文件里设置的包名匹配。
组件名是可选的。如果设置了,intent对象分派到目的类的实例。如果不设置,Android使用intent的其它信息来本地化合适的目标——阅读本文后面的 Intent Resolution部分。
组件的名字通过函数setComponent()、setClass()、setClassName()设置,通过函数读取getComponent()。
动作
需要执行的动作的名字——或在广播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. |
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类的描述,关于为产生动作而预定义的常量。其它动作被定义于Android的API。你可以在自己的程序里定义自己的动作字符串来激活组件。你的这些发明,需要包含程序包作为前缀——例如:com.example.project.SHOW_COLOR。
动作定义了intent是什么样的结构——指定数据和扩展域——如一个函数名决定一个参数集、一个返回值。由于这个原因,使用动作名的好方法是尽可能的明确,它们配对的和intent里的其它域不一样。换句话说,不是孤立的定义动作,而是定义一个协议以便你的组件可以处理intent对象。
intent里的动作是通过 setAction()函数设置,通过getAction()函数读取。
数据
数据的URI和MIME类型的数据。不同的动作和不同的数据配对。例如:如果动作域是ACTION_EDIT,数据域需要包含文档的URI以便显示,编辑。如果动作是ACTION_CALL,数据域需要是一个带拨号的号码的tel: URI。相同的,如果动作是ACTION_VIEW数据域是http: URI,接收Activity需要调用并下载、显示URI引用的任何数据。
intent和组件匹配是处理数据的能力,它通常是从附加在URI的信息知道数据类型的(数据的MIME类型)。例如:组件能显示图形数据不能用来播放音频文件。
很多时候,数据类型可以通过URI推断——但是 content:URI特殊,它指示数据数据被本地化于设备并通过一个内容提供者来控制(阅读separate discussion on content providers)。但是,这种类型还是可以显示的在intent里设置。setData() 函数指定数据作为一个URI, setType()指定它为一个MIME类型,setDataAndType()指定它是URI也是MIME类型。 getData()函数读取URI, getType()读取类型。
类型
包含附加信息的字符串,信息是需要处理intent的组件的类型。任何类型的数字描述可以放入intent对象。就像对动作一样,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 button 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. |
阅读intent类的描述,里面有完整的类别列表。
addCategory() 放置一个intent里的类别,removeCategory()删除之前添加的,getCategories()获取当前所有的类别。
扩展
附加键值对信息,这个键值会分派给处理intent的组件。一些动作和特定的数据URI匹配,一些和特定的扩展匹配。例如:一个ACTION_TIMEZONE_CHANGED的intent有一个time-zone扩展域指明新的时区,ACTION_HEADSET_PLUG有一个state扩展的状态域指明耳机插入或拔出,name也有一个域来指明耳机的类型。如果你发明一个动作SHOW_COLOR ,颜色值在扩展键值对里设置。
intent有一系列的put…() 函数来插入各种类型的数据和一系列get…()函数来获取各种类型的数据。对Bundle 对象,这些函数是并行的。事实上,可以使用函数putExtras()
和函数getExtras()
来把数据作为Bundle读取、插入。
标志
各种排序的标志。指示Android如何启动Activity(例如:Activity属于那个任务)启动后如何处理(例如:是否属于现在Activity 的列表)。这些标志在intent类里定义。
和平台相关的Android系统和程序使用intent来发送系统的广播、激活系统定义的组件。和intent激活系统组件相关的内容,在list of intents 。
intent解决
intent可以分为两类:
通过名字指定目标组件(阅读component name field,文档里有一个值的集合)。其它程序的开发人员不需要知道组件名,显式的intent用于程序内部消息——如:Activity启动一个下属服务或启动一个姊妹Activity。
隐式intent没有命名一个目标(组件名是空的),隐式intent通常用来激活其它程序的组件。 Android分派一个显式的intent给指定目标类的实例。除了intent对象,没可以匹配获取intent组件的组件名。
隐式intent需要不同的策略。没有指定目标,Android系统需要查找最适合处理intent的组件(或几个组件)——一个单一的Activity或服务来执行请求的动作或设置广播接收器来响应广播通知。通过把intent对象的内容和intent管理器比较,判断那个组件是潜在的接收者。过滤器提供组件的能力并且划定它可以处理的intent。它开启可以接收隐式intent的组件类型。如果组件没有intent过滤器,它仅仅可以接收显式的intent。含有过滤器的组件既可以接收隐式intent也可以接收显式intent。
intent过滤器里测试intent对象的三个方面:
* 动作
* 数据(URI和数据类型)
* 类别
通过扩展和标志不可以确定那个组件接收intent。
intent过滤器
为了通知系统那个组件、Activity,service,广播过滤器可以处理intent,系统可以有多个intent过滤器。每个过滤器描述一个组件的能力,一个不处理的intent集合——仅仅是不处理隐式intent(这些不命名一个目标类)。一个显示intent总是分派给它的目标,不管它包含什么内容;过滤器这个时候不起作用。但是一个隐式intent仅当它可以通过一个组件的过滤器,次才被分派给这个组件。
组件区分每个过滤器的功能,每个展示给用户的界面,例如:Note Pad程序的Activity有两个过滤器——一个启动一个指定的note,用户可以看或编辑,另一个启动一个新建的、空的note,用户可以编辑并且保存。(Note Pad所有的过滤器在Note Pad Example 一节里有描述。)
过滤器和安全性
Intent过滤器没有可靠的安全性。当它打开一个接收处理显式intent的组件,它并没有阻止隐式intent。尽管一些过滤器限制组件可以处理哪些动作和数据,有时额可以吧不同的动作和数据放入一个显式的intent,并把组件作为目标。
Intent过滤器是IntentFilter
类的实例。然而,Android系统在启动组件前必须知道组件的能力,intent过滤器是在manifest文件AndroidManifest.xml
里作为<intent-filter>
元素建立而不是在java代码里。(有一个特例是:广播接收器的过滤器,它是通过Context.registerReceiver()
函数动态的注册;它被作为IntentFilter对象创建。)
一个过滤器有动作域、数据域、intent对象类别域。一个显式的intent测试这三个域。并派送给拥有过滤器的组件,必须通过三个测试。如果有一个测试失败,Android系统都不会分派——至少不是过滤器的基础。然而,如果组件有多个intent过滤器,不分派给一个组件也会分派给另一个组件。
以上三个测试的细节如下:
动作测试
manifest文件里的<intent-filter>
元素作为 <action>
子元素列举动作。例如:
1 2 3 4 5 6 |
|
就像例子显示的,当一个intent对象名仅仅是一个单独的动作,一个过滤器列举更多。这个列表不可以为空;管理器至少需要包含一个<action>
元素,或它将阻止所有的intent。
传递这个测试,在intent里指定的动作必须匹配管理器列表里的动作。如果对象或过滤器不指定动作,结果如下:
如果过滤器列举动作失败,没有和intent匹配的动作,因此所有的intent测试都失败。没有intent可以打通这个管理器。
另一方面,intent对象不知道一个动作,并自动的传递测试——只要过滤器包含至少一个动作。
类型测试
1 2 3 4 5 6 |
|
注意:常量描述的动作和类别在manifest文件里没有使用。使用的是完整的字符串。例如:上面例子里的字符串android.intent.category.BROWSABLE反映的是本文档之前提到的常量CATEGORY_BROWSABLE 。同样地,字符串android.intent.action.EDIT反映的是常量ACTION_EDIT。
传递一个intent给类别测试,每个intent里的类别需要匹配一个过滤器里的类别。过滤器列举附加的类别,但是它不可以忽略任何intent里的类别。
因此,一个没有类别的intent对象总是可以通过测试,不论过滤器里是什么内容。大多数情况是这样。然而,有一个特例,Android处理传递给startActivity()的显式intent就像它包含了一个类别:android.intent.category.DEFAULT( CATEGORY_DEFAULT常量)。因此,想接收显式intnet的Activity需要在intent过滤器里包含。(设置了android.intent.action.MAIN和android.intent.category.LAUNCHER的过滤器是特殊情况。它们把Activity标记为任务前运行,并且会在启动画面上显示。它们在类别列表里包含android.intent.category.DEFAULT,但是并不必须。)更多过滤器请阅读 Using intent matching。
数据测试 和动作、类别一样,intent过滤器指定的数据包含一个子元素。这种情况下,子元素可以多次出现,或不出现。例如:
1 2 3 4 5 |
|
每个 元素可以指定一个URI和一个数据类型(MIME类型)。有各种属性——scheme、host、port、path ——是URI的每个部分:
scheme://host:port/path
例如:下面的URI
content://com.example.project:200/folder/subfolder/etc
方案是content
,主机是com.example.project
,端口是200
,路径是folder/subfolder/etc
。主机和端口构成URIauthority;如果主机没有指定,端口也忽略了。
每个属性都是可选的,但是它们并不是独立的;一个authority哟啊有意义,一个方案需要被指定。一个路径要有意义,一个方案和authority需要被指定。
当intent对象里的URI和过滤器里指定的URI比较的时候,仅仅比较过滤器里有的部分。例如:如果过滤器仅仅指定方案,匹配方案的URI都符合条件。如果过滤器指定方案、权限没有指定路径,那么忽略路径的情况下,和方案、权限匹配的URI都符合条件。如果过滤器指定方案、权限、路径,仅当方案、权限、路径都匹配的时候才符合。然而,一个路径里包含宽字符仅需要部分匹配。
<data>
元素的type 属性指定数据的MIME类型。一般在过滤器里的情况比在URI里多。Intent对象和过滤器可以使用*来表示子域——例如:text/*
或audio/*
——表示匹配任何子域。
数据测试对比intent对象里的URI和数据类型和过滤器里的。规则如下:
a.仅仅当过滤器不指定任何URI或数据类型的时候,才会把一个没有包含URI或数据类型的intent对象传递给测试。
b.仅仅如它的URI匹配一个过滤器里的URI并且过滤器没有指定类型(这个类型不可以通过URI推断),会把一个包含URI不包含数据类型的intent传递给测试。这种情况仅出现于URI如mailto:
和tel:
这样不引用实际数据的情况。
c.如果过滤器列出相同的数据类型并不指定URI,一个包含数据类型不包含URI的intent会传递给测试。
d.包含URI和数据类型的intent对象(或可以通过URI推断数据类型)如果它的类型和过滤器的类型的列表你的匹配,那么就测试数据类型部分。如果它的URI和过滤器列表的匹配,或包含content:
、file:
、没有指定URI,那么就测试URI部分。换句话说,组件可以断定如果过滤器列表仅仅有数据类型那么它支持content:
和 file:
数据。
如果intent通过过滤器传递几个Activity或服务,会询问用户激活那个组件。如果没有发现目标会产生一个异常。
通常的例子
上面最后一条规则,规则(d)显示了我们希望组件可以从一个文件或内容提供者李获取局部数据。因此,它们的过滤器仅需要列出数据类型,不需要显示的指定主题名为 file:
或 content:
。这是特例。 <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
:
1 2 3 4 |
|
使用intent匹配
通过intent和intent过滤器的匹配,不仅可以发现目标组件,还可以发现设备上的组件集。例如:Android系统启动程序,顶层屏幕显示用户启动的程序,查找intent过滤器指定android.intent.action.MAIN
动作和android.intent.category.LAUNCHER
类别(前一节有说明)的Activity。然后显示图标并且标记这些Activity。同样地,它通过查找过滤器里有android.intent.category.HOME
的Activity来发现home屏幕。
你的程序可以使用相同的思路来匹配。PackageManager
有一系列query...()
函数可以返回intent可以访问的所有组件,一系列resolve...()
函数来决定那个是最适合相应intent的组件。例如:queryIntentActivities()
返回可以处理intent的Activity,queryIntentServices()
函数返回服务列表。;它们仅仅列出可以响应的那个。有一个类似的函数,queryBroadcastReceivers()
,广播接受者使用的。
例子:Note Pad
Note Pad是一个简单的允许用户浏览note列表,查看每一项细节,编辑每个note,添加一个新的note的程序。查看manifest文件里intent过滤器的声明。(如果你用离线的SDK,你可以看到这个程序的所有源代码,包括manifest文件,路径是:<sdk>/samples/NotePad/index.html
。如果你查阅在线文档,源代码在:Tutorials and Sample Code 。)
在它的manifest文件里,Note Pad程序声明了三个Activity,每个都有一个intent过滤器。也声明一个内容提供者来管理note数据。Manifest文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
|
第一个Activity,NoteList,和其它Activity不同,它操作一个note的目录(note列表)而不是一个单一的note。通常是充当程序的初始化界面。它额可以依据下面三个intent过滤器来做三件事。
1 2 3 4 |
|
这个过滤器声明指向Note Pad程序的实体。标准动作是一个在intent里不需要任何信息(比如:没有数据)的实体点,类别说的是实体点需要在程序的启动里列出。
1 2 3 4 5 6 7 |
|
这个过滤器声明Activity可以对节点做的事情。允许用户浏览或编辑词典(通过VIEW
和EDIT
动作),或是获取节点(通过PICK
动作)。
<data>
元素的mimeType属性指定这些动作操作的数据类型。它指示了Activity可以从保存Note Pad数据(vnd.google.note
)的内容提供者里获取零个或多个Cursor项(vnd.android.cursor.dir
)。启动Activity的intent需要包含一个 content:URI
来指定Activity需要打开的扩展数据。
主要这个过滤器里指定了DEFAULT
类别。因为Context.startActivity()
和Activity.startActivityForResult()
函数处理所有的intent就像它们包含DEFAULT
类别——仅仅有两个特例:
- 显式知道目标Activity的intent。
- 包含MAIN 动作和LAUNCHER 类别的intent。
因此,DEFAULT
类别需要所有的过滤器——处理有动作MAIN
和类别LAUNCHER
的。(intent过滤器不需要显示的intent。)
1 2 3 4 5 |
|
过滤器描述了Activity可以翻译一个用户选中的note,用户没有从指定的目录里查询。
动作和动作是类似的。Activity返回用户选中的note的URI。(调用startActivityForResult()
启动NoteList Activity,)然而,调用者指定数据类型,而不是用户从目录里获取数据类型。
数据类型,vnd.android.cursor.item/vnd.google.note
,指明了Activity可以返回的数据类型——一个独立的节点的URI
。从返回的URI
,调用者可以从保存Note Pad 数据(vnd.google.note
)的内容提供者里获取一个项的Cursor(vnd.android.cursor.item
)
换句话说,之前过滤器里的PICK
动作,数据类型暗示了Activity显示给用户的数据类型。对于GET_CONTENT
过滤器,它指示了Activity可以返回给调用者的数据类型。
给定这些功能,下面的intent将解决NotePad Activity:
动作:android.intent.action.MAIN
没有数据指定的情况下启动Activity。
动作:android.intent.action.MAIN
类别:android.intent.category.LAUNCHER
没有数据选择域的情况启动Activity。
动作:android.intent.action.VIEW
数据:content://com.google.provider.NotePad/notes
调用Activity显示content://com.google.provider.NotePad/notes
下note的列表。用户可以通过列表浏览和获取没一个项的信息。
动作:android.intent.action.PICK
数据:content://com.google.provider.NotePad/notes
调用Activity来显示content://com.google.provider.NotePad/notes
下的note的列表。用户可以从列表里选择一note,Activity将返回该项的URI来启动一个NoteList Activity。
动作: android.intent.action.GET_CONTENT
数据类型:vnd.android.cursor.item/vnd.google.note
调用Activity来提供一个单独的Note Pad数据项。
第二个Activity,NoteEditor,显示一个note给用户,并且允许用户编辑。在intent的过滤器里描述了它可以做的两件事情:
1 2 3 4 5 6 7 |
|
Activity的第一个、最基本的功能是允许用户和一个note交互——或者浏览或者编辑。(EDIT_NOTE类对EDIT来说是一个。)intent需要包含和数据的URI并和MIME类型vnd.android.cursor.item/vnd.google.note
匹配——URI是独立的、指定note的URI。需要是一个通过PICK
或GET_CONTENT
动作从NoteList Activity里返回的URI。过滤器列出了DEFAULT
因此Activity可以通过intent来启动,这个intent没有显示的指定NoteEditor类。
1 2 3 4 5 |
|
Activity的第二个作用是允许用户创建新的note,并插入到已有的note字典里。Intent需要包含和数据的URI
匹配的MIME类型vnd.android.cursor.dir/vnd.google.note
——词典的URI
需要被替换。
给定这些功能,接下来的intent可以NoteEditor的Activity:
动作:android.intent.action.VIEW
数据:content://com.google.provider.NotePad/notes/ID
调用Activity显示通过ID指定的note的内容。(关于 content:URI
指定群成员的细节知识,阅读Content Providers。)
动作:android.intent.action.EDIT
数据:content://com.google.provider.NotePad/notes/ID
调用Activity来显示通过ID 标识的note的内容。如果用户保存修改,Activity更新在内容提供者里的note的数据。
动作:android.intent.action.INSERT
数据:content://com.google.provider.NotePad/notes
调用Activity在 content://com.google.provider.NotePad/notes
的note列表里创建一个新的、空的note。允许用户编辑它。如果用户保存note,note的URI
返回给调用者。
最后一个Activity,TitleEditor,允许用户编辑note的标题。通过调用Activity(显式的设置它的intent的组件名)来实现,而不是使用intent过滤器。展示如何对已经存在的数据执行操作:
1 2 3 4 5 6 7 |
|
这个intent过滤器可以使用一个用户定义的名为com.android.notepad.action.EDIT_TITLE
的动作。调用一个指定的note(数据类型是vnd.android.cursor.item/vnd.google.note
),如之前的VIEW
和EDIT
动作。然而,Activity显示包含着note数据里的标题,不包含note内容本身。
支持类DEFAULT
,标题编辑器支持其它两个标准的类:ALTERNATIVE
和SELECTED_ALTERNATIVE
。这些类别暗示了Activity可以在一个菜单选项里显示给用户(比如:LAUNCHER
类别暗示了Activity需要在程序启动的时候显示给用户)。注意:过滤器也提供显示的标签(通过:android:label="@string/resolve_title"
)来控制Activity,这个Activity用来替代显示数据的动作。(更多关于这些类别的信息、建立菜单选项,阅读 PackageManager.queryIntentActivityOptions()
和Menu.addIntentOptions()
函数。)
给定这些功能,下面的intent可以解决TitleEditor Activity:
动作: com.android.notepad.action.EDIT_TITLE
数据:content://com.google.provider.NotePad/notes/ID
调用Activity来显示和ID关联的标题,允许用户编辑标题。