您的位置:首页 > 产品设计 > UI/UE

(API GUIDE 3)Intents and Intent Filters

2015-02-17 18:27 211 查看
Intents and Intent Filters

一个intent是一个消息传递对象,你可以用它来从另一个应用成分请求一个动作。即便intents可以通过多种方式促成通信,但这里有三个基本的用例:

1、启动一个活动:

一个活动代表应用中一个独立的界面。你可以通过传递一个intent给startActivity()启动一个新的活动实例。intent描述要启动的活动并携带任何必要的数据。

如果你想在活动结束的时候从活动接受一个结果,调用startActivityForResult().你的活动接受一个你的活动的onActivityResult()回调函数的分离的intent对象作为结果。更多的信息,详见Activities Guide.

2、启动一个服务:

服务是运行在后台操作的,没有用户接口的成分。你可以启动一个服务来通过传递Intent给startService()执行一个一次性的服务(例如下载一个文件).Intent描述将要启动的服务并且携带必要的数据。

如果服务被设计带有一个客户服务端接口,你可以从另一个成分通过传递一个intent到bindService()绑定服务.更多的消息,详见Services guide.

3、传递一个广播:

广播是所有的应用都能接收到的消息。系统为事件传递各种各样的广播,例如当系统启动或者设备开始充电的时候。你可以通过传递一个intent给sendBroadcast(),sendOrderedBroadcast(),或者sendStickyBroadcast()传递一个广播给其他的应用。

Intent种类

这里有两种intents:

显式的intents:指定通过名字指定将要启动的成分(完全合法类名)。你将会典型地使用一个显式的intent来启动一个你自己的应用中的成分,由于你知道活动或者是你想启动的服务的的类名。例如,启动一个新活动回应一个用户动作或者启动一个服务后台下载一个文件。

隐式的intents:没有命名一个特定的成分,但是取而代之的是申明一个通用的动作来执行,这个动作允许来自其他应用的成分获得它的句柄。例如,如果你希望在地图上展示给用户一个地址,你可以使用一个隐式的intent来请求另一个合适的应用来在地图上显示一个特定的地点。

当你生成一个显式的intent来启动一个活动或者服务的时候,系统立刻启动在intent对象中指定的应用的成分。

当你生成一个隐式的intent的时候,安卓系统找到合适的成分并通过比较intent的成分和manifest 文件里申明的设备上的其他的应用中的intent过滤器。如果intent和intent过滤器匹配,系统启动那个成分并且把它头送给intent对象。如果有多个intent过滤器都匹配,系统显示一个对话框,用户可以选择一个应用。

intent过滤器是一个位于应用manifest文件里的制定成分希望接受什么样的intent对象的表达式。举例说明,通过为一个活动申明一个intent过滤器,可以使得其他应用能够直接启动你的带有特定intent的活动。同样的,如果你没有为活动申明任何intent过滤器,那么它只会启动显式的intent.

注意!!

为了确保你的应用是安全的,启动一个服务的时候总是使用一个显式的intent,不为你的应用申明intent过滤器。使用一个隐式的intent启动服务是一个安全灾难,因为你永远不会知道什么服务会响应intent,并且用户看不见哪个服务启动了。从安卓5.0开始,如果你用一个隐式的intent调用bindService(),系统会抛出一个异常.

建立一个intent

intent对象携带着安卓系统用来决定启动那个成分的信息(比如准确的应该接受intent的成分名称或者成分类别),为了正确地执行动作,还加上接受的成分使用的信息(诸如要执行的动作和基于的数据)。

包含在intent中的基本信息如下

1、成分名:

将要启动的成分的名字。

这是可选的,但是它是使一个intent显式的重要信息,意义是,intent应该仅仅被投送到被成分名称定义的应用成分。。如果没有成分名称这一项,intent是隐式的并且系统根据其他的intent信息决定那个成分应该接受intent(诸如动作,数据和分类——下面介绍)。所以如果你需要启动一个应用中的特定的成分,你应该指定一个成分名称。

贴士:当启动一个服务,你应该总是指定成分名称。否则,你不能确定哪个成分将会响应intent,并且用户也无法看见哪个服务在启动。

intent的这个域是一个成分名对象,你总是应该确定使用一个完全合法的目标成分的类名,包括应用的包名。例如,com.example.ExampleActivity。你可以用setComponent(),setClass(),setClassName(),或者式intent构造器设置成分名。

2、动作:

是一个指定了通用动作(诸如查看,选择)的字符串。

在广播intent的情况下,这是发生的或者被报道的动作。这个动作在很大程度上决定了剩余的intent如何建立——特别是什么包含在数据和其他东西里。

你可以自己指定你自己的在你的应用中被intent执行的动作(或者被其他应用用来调用你的应用里的成分的的动作),但是你应该经常使用被intent类或者其他框架类定义的动作常数。这里是一些常规的启动一个活动的动作:

ACTION_VIEW

当你有一些活动可以给用户显示的信息的时候,在一个intent用startActivity()使用这个动作,诸如在一个画廊应用中查看照片或者在一个地图应用中查看地图。

ACTION_SEND

也称作“共享”intent,你应该在intent中用startActivity()使用这个动作,例如一个邮件应用或者社交共享应用。

查看Intent类引用寻找更多的定义通用动作的常数。其他动作被定义在安卓框架的其他地方,诸如在动作settings查找在系统设置应用中打开特定屏幕的动作。

你可以用setAction()或者intent构造器为一个intent指定动作。

如果你定义你的动作,请确保包含了应用的包名作为前缀。例如

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";

3、数据

引用将要被操作的数据的URI或者是MIME类型。提供的数据类型通常被被intent的动作决定。例如,如果动作式ACTION_EDIT,数据应该包含文档的URI以便被编辑。

当产生一个intent,除了他的URI,指定数据的类型(MIME类型)也是很重要的。例如,一个显示图像的活动坑内不能播放音频文件,即使URI格式可能式类似的。所以指定你的数据的MIME类型有助于安卓系统找到最佳的成分来接受你的intent。然而,MIME种类有时候可以从URI中推测出来——特别是当数据是content的时候(content:被ContentProvider控制的指示数据在设备商的定位的URI,可以使得MIME类型对于系统可见)。

只设置数据URI,调用setData()。只设置MIME种类,不要调用setData()和setType()因为他们每个都会飞起另一个的值。总是使用setDataAndType()来设置URI和MIME种类。

4、类别

包含应该处理intent的成分的种类的额外信息的字符串。任何数量的种类描述器可以被放置在一个intent中,但是大多数的intents不能请求一个类别。这里是一些常见的类别:

CATEGORY_BROWSABLE

允许自己被一个网页浏览器启动来通过一个链接显示引用数据诸如图像或者一个电子邮件消息的一个目标活动类型。

CATEGORY_LAUCHER

被列举在系统应用安装器中的一个任务初始化活动类型。

查看Intent类描述器寻找全部类别列表。

你可以用addCategory()添加指定一个类别。

上面列举的这些特征(成分名称,动作,数据,和类别)代表了定义的一个intent的特征,通过阅读这些特征,安卓系统能够解析出它应该启动哪个成分。

然而,一个intent可以携带不影响它如何解析一个应用成分的额外的信息。一个intent也可以提供:

附加

携带完成要求的动作所需要的额外信息的关键值对。正如一些动作使用特定种类的数据URI一样,一些动作也使用特定的附加值。

你可以使用各种各样的putExtra()方法添加附加值数据,每个方法接受两个参数:关键名和值。你也可以产生一个带有所有附加数据的Bundle对象,然后用putExtras()方法把Bundle对象插入到intent中。

例如,当生成一个intent用ACTION_SEND发送一封邮件的时候,你可以用EXTRA_EMAIL指定接收者,并且可以用EXTRA_SUBJECT键值指定“主题”。

intent类指定许多EXTRA_*常数来同意数据类型。如果你需要申明你自己的附加的键值(对于你的应用接收到的intents),确保包括了你的应用的包名作为前缀。例如:

static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";

旗标:

定义在intent类里的旗标起到类似于intent元数据的功能。旗标可能指定安卓系统如何启动一个活动(例如,活动应该的属于哪个任务)以及如何在它被启动之后如何对待(例如,它是否属于最近活动的列表)。

更多的信息,详见setFlag()方法。

显式intent举例

显式的intent是用来启动一个指定的应用成分,诸如一个应用中特定的活动或者服务。为了生成一个显式的intent,为intent对象定义成分名——其他所有的intent属性都是可选的。

例如,如果你在你的应用中建立一个服务,称作DownloadService,设计用来从网站下载一个文件,你可以用下面的代码启动它:

// Executed in an Activity, so 'this' is the Context

// The fileUrl is a string URL, such as "http://www.example.com/image.png"

Intent downloadIntent = new Intent(this, DownloadService.class);

downloadIntent.setData(Uri.parse(fileUrl));

startService(downloadIntent);

Intent(Context,Class)构造器提供了应用Context和一个成分的一个类对象。这样,这个intent显式地启动应用中的DownloadService类。

更多关于建立和启动一个服务的消息详见Services指引。

隐式intent举例:

一个隐式的intent指定一个动作,这个动作可以激活任何支持执行该动作的设备中的应用,但是如果你的应用无法执行这一动作,隐式intent就有用了,但是还有其他的应用可选,这样你就希望用户来选择。

例如,如果你有希望用户和别人共享的内容,使用ACTION_SEND动作生成一个intent并添加指定共享内容的附加值。当你用哪个intent调用startActivity(),用户挑选一个应用来共享内容。

注意!!有种可能是一个用户没有能够处理你发送给startActivity()的应用。这种情况下,调用将会失败,应用会崩溃。为了验证活动会收到intent,在你的intent对象上调用resolveActivity()。如果结果是非空,那么至少一个应用可以处理intent,调用startActivity是安全的。如果结果是空,你不应该使用intent并且,如果可能的话,你应该关闭发出这一intent的特色。

// Create the text message with a string

Intent sendIntent = new Intent();

sendIntent.setAction(Intent.ACTION_SEND);

sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);

sendIntent.setType(HTTP.PLAIN_TEXT_TYPE); // "text/plain" MIME type

// Verify that the intent will resolve to an activity

if (sendIntent.resolveActivity(getPackageManager()) != null) {

startActivity(sendIntent);

}

贴士:这种情况下,URI没有用,但是intent的数据类型被申明来制定附加值携带的内容。

当startActivity()被调用,系统检查所有的已经安装的应用爱决定哪些能够处理这种类型的intent(带有“text/plain”数据和ACTION_SEND动作)。如果仅有的一个应用能够处理它,应用迅速打开并获得intent.如果多个活动接受了intent,系统会显示一个对话框以便应用能够选择使用哪个应用。

强推一个应用选择器

当有一个以上的应用响应你的隐式intent,用户选择哪个应用来使用并且设置那个额应用为这个动作的默认应用。当用户总是希望从此使用同一个应用执行一个动作例如打开一个网页(用户跟喜欢只有一个浏览器)的时候,这是很好的。

可是,如果多个应用可以相应intent并且用可能希望每次使用一个不同的应用,你应该显式地显示一个选择对话框。选择对话框询问用户选择哪个应用来处理这个动作(用户不能微动作选择一个默认应用)。例如,当你的应用使用ACTION_SEND处理“share”动作,用户可能希望根据他们的当前状态使用不同的应用来共享,所以你应该总是使用选择对话框,如图2所示。

为了展示选择器,使用createChooser()生成一个Intent并且把它传递给startActivity().例如:

Intent sendIntent = new Intent(Intent.ACTION_SEND);

...

// Always use string resources for UI text.

// This says something like "Share this photo with"

String title = getResources().getString(R.string.chooser_title);

// Create intent to show the chooser dialog

Intent chooser = Intent.createChooser(sendIntent, title);

// Verify the original intent will resolve to at least one activity

if (sendIntent.resolveActivity(getPackageManager()) != null) {

startActivity(chooser);

}

这显示了一个带有响应传递给createChooser()方法的intent的应用列表的对话框,并且这个对话框使用提供的文本作为对话框标题。

接收一个隐式的intent

为了通告你的应用可以接受什么隐式的intent,用<intent-filter>元素为你的每个应用成分申明一个或多个intent过滤器。每个intent过滤器根据intent的动作,数据和类别指定了它接收到的intent的种类。系统将会仅当intent可以通过你的某个intent过滤器传递的时候传递一个隐式的intent给你的应用成分。

贴士:一个显式的intent总会传递给它的目标,不论任何intent过滤器成分的申明。

应用成分应该为每一个特定的它可以做的工作申明分离的过滤器。例如,在一个图片画廊应用的活动可能有两个过滤器:查看图片,编辑图片。当活动启动,它检查intent并且根据intent中的信息决定如何行动(例如是否需要显示一个编辑控制栏)。

每个intent过滤器都被<intent-filter>元素定义在了应用的manifest 文件里,包裹在对应的应用成分(例如<activity>元素)。在<intent-filter>里,你可以指定intent的种类来接受使用一个或多个元素:

<action>

声明接受的intent动作,在name属性里。这个值必须是一个动作的字符串值,不是类常量。

<data>

声明接收到的数据种类,使用一个或者多个指定数据URI(scheme,host,port,path等等)和MIME类型的各个方面的属性。

<category>

声明接收到的类别类型,位于name属性。这个值必须是一个动作的字符串值,而不是类常量。

贴士:为了接收到隐式的intent,你必须在intent中包含CATEGORY_DEFAULT类别。方法startActivity()和startActivityForResult()像对待他们声明了CATEGORY_DEFAULT类别一样对待所有的Intents。如果你没有在你的intent过滤器中声明这个类别,没有隐式的intents会被解析到你的活动。

例如,这里是一个使用intent过滤器的活动声明,这个声明在数据种类是文本的时候来接受一个ACTION_SEND的intent。

<activity android:name="ShareActivity">

<intent-filter>

<action android:name="android.intent.action.SEND"/>

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

<data android:mimeType="text/plain"/>

</intent-filter>

</activity>

生成一个包含超过一个<action>,<data>,<category>实例的过滤器是没问题的。如果你这样做了,你仅仅需要确定成分可以处理这些过滤器元素中任何一个和所有的组合。

当你希望处理多个intent种类,但是仅仅在特定的动作,数据,类别的结合的时候,你需要生成多个intent过滤器。

一个隐式的intent被进行和过滤器的匹配测试方式是比较intent和过滤器中的每个元素。在投递到成分之前,intent必须通过三项测试。如果它没能匹配哪怕一个测试,安卓系统不会投递intent给它。然而,由于一个成分可能有多个intent过滤器,一个intent没能通过一个过滤器的测试还可能会被传递到另一个过滤器。更多的关于系统如何解析intents信息详见下文 Intent Resolusion.

注意:为了避免无意中运行一个不同的应用的服务,总是使用一个显式的intent来启动你自己的服务并且不需要为你的服务声明。

贴士:对于所有的活动,你必须在manifest文件中声明你的intent过滤器。可是,广播接收者的过滤器可以通过调用registerReceiver()被动态注册。你可以之后通过unregesterReceiver()解注册这个接受者。这样做允许你的应用在运行到特定的时间监听特定的广播。

限制成分访问:

使用intent过滤器不是一个安全的方法,即使intent过滤器只会响应特定种类的隐式的intent,另一个应用也可能潜在地通过一个intent启动你的应用成分(如果开发者指定了这个成分的名字之后)。如果这个成分只在你自己的应用中启动很关键,设置exported属性为false.

过滤器的例子:

为了能更好地理解一些intent过滤器的行为,看下面的从社交共享软件的manifest文件中截取的片段。

<activity android:name="MainActivity">

<!-- This activity is the main entry, should appear in app launcher -->

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

<activity android:name="ShareActivity">

<!-- This activity handles "SEND" actions with text data -->

<intent-filter>

<action android:name="android.intent.action.SEND"/>

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

<data android:mimeType="text/plain"/>

</intent-filter>

<!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->

<intent-filter>

<action android:name="android.intent.action.SEND"/>

<action android:name="android.intent.action.SEND_MULTIPLE"/>

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

<data android:mimeType="application/vnd.google.panorama360+jpg"/>

<data android:mimeType="image/*"/>

<data android:mimeType="video/*"/>

</intent-filter>

</activity>

第一个活动,MainActivity,式应用的主接入点——当用户通过启动图标启动应用的时候活动打开。

1、ACTION_MAIN动作指示这是个主接入点并且不期望任何intent数据。

2、CATEGORY_LAUNTCHER类别指示活动的图标应该被放置在系统的应用启动器中。如果<activity>元素没有用icon指定一个图标,那么系统从<application>元素中选择使用图标。

为了使活动出现在应用启动器中,这两个必须成对出现。

第二个活动,ShareActivity,被期望能够方便共享文本和媒介内容。即使用户可能通过从MainActivity导航进入这个活动,他们也可以从其他的发出一个隐式的intent并且能够匹配这两个intent过滤器中的一个的应用直接进入ShareActivity.

贴士:MIME类型,application/vnd.google.panorama360+jpg,是一个特别的指定了全景照片的数据类型,你可以使用Google panorama APIs处理。

使用一个等待intent

PendingIntent对象是一个在一个Intent对象周围的包装。PendingIntent的基本的目的是给一个外部的应用权限来使用包含的intent就像它是从你的应用的进程执行的一样。

等待intent的主要使用场景是:

1、声明一个当用户在你的应用窗口(Home界面应用执行intent)执行一个动作的时候被执行的intent。

2、声明一个当用在你的通知栏执行动作的时候执行的intent(安卓系统的NotificationManager执行Intent).

3、声明一个在未来特定时刻执行的intent(安卓系统的AlarmManager执行intent).

由于每个intent对象被设计来处理一个特定种类的应用成分(要么是一个Activity,一个服务,或者是一个BroadcastReceiver),所以PendingIntent也必须用同样的想法生成。当使用一个等待intent的是偶,你的应用将不会调用诸如startActivity()来执行intent。你必须当你通过调用各自的生成器方法生成PendingIntent的时候声明一个有意图的成分种类:

用PendingIntent.getActivity()生成启动一个活动的intent.

用PendingIntent.getService()生成一个启动服务的intent.

用PendingIntent.getBroadcast()生成一个启动一个广播接收者的intent.

除非你的应用正在从其他应用接受intents,上述的产生那个一个PendingIntent方法是仅有的你可能使用的生成PendingIntent的方法。

每个方法需要参数:当前应用的Context,你希望打包的intent,一个或者多个指定如何使用intent的旗标(例如是否intent可以被使用多次)。

更多的关于使用等待intents的信息详见每个用例文档,例如Notifications和App Widgets API guides.

Intent 解析

当系统接收到一个隐式的intent来启动一个活动的时候,它通过比较intent和intent过滤器,基于下面三条寻找对于intent最佳的活动:

1、intent活动

2、intent数据(URI和数据种类)

3、intent类别

下面的部分从在manifest文件中声明的intent过滤器的角度描述一个intents是如何匹配到一个合适的成分的。

动作测试

为了指定接受的intent动作,一个intent过滤器可以声明宁哥或者更多的<action>元素。例如:

<intent-filter>

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

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

...

</intent-filter>

为了通过这个过滤器,intent中指定的动作必须匹配列举在过滤器中的动作之一。

如果过滤器没有列举任何动作,intent没有动作匹配,所有的intent都会匹配失败。然而,如果一个intent没有指定一个动作,它将会通过测试(制药过滤器至少包含一个动作)。

类别测试

为了指定接收的intent类别,一个intent过滤器可以声明零个或者更多的<category>元素.例如:

<intent-filter>

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

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

...

</intent-filter>

对于一个通过类别测试的intent,每个intent中的类别必须匹配过滤器中的一个类别。反之不是必要的——intent过滤器可能声明比在intent指定的更多的类别的情况下intent仍然通过测试。所以,一个没有类别的intent应该总是会通过这个测试,无论过滤器声明的是什么类别。

贴士:安卓自动地对所有传给satrtActivity()和startActivityForResult()的隐式的intents应用CATEGORY_DEFAULT类别。所以如果你希望你的活动接受隐式的intents,它必须包括一个类别对应intent过滤器中的”android.intent.category.DEFAULT”(见之前的<intent-filter>例子)。

数据测试

为了制定接受的intent数据,一个intent过滤器可以声明零个或者多个<data>元素。例如:

<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,portpath——对于URI的每部分:

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

例如

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

在这个URI中,机制式content,主机是com.example.project,端口号式200,路径是folder/subfolder/etc。

这些属性中每个在<data>元素里都是可选的,但是这里有些线性依赖:

1、如果机制没有被指定,主机也要被忽略。

2、如果主机没有被指定,端口被忽略。

3、如果机制和主机都没有被指定,路径被忽略。

当intent中的URI和过滤器中指定的URI比较的时候,它只比较包含在过滤器中的部分URI.例如:

1、如果过滤器只指定一个机制,所有带有那个机制的URI会匹配过滤器。

2、如果过滤器指定一个机制和一个权柄但是没有路径,所有带有同样机制和权柄的URI匹配过滤器,不论他们的路径是什么。

3、过滤器指定一个机制,一个权柄,和一条路径,只有三者都一样的URI才会匹配。

贴士:一个路径指定可以包含一个通配符星号来请求仅仅一个部分的路径匹配。

数据测试比较所有intent中的URI和MIME类型和过滤器指定的URI和MIME类型。下面是规则:

a.一个不带URI和MIME类型的intent只有过滤器没有指定任何URI或者MIME类型的时候才会通过测试。

b.包含URI但是不包含MIME类型(显示的或者从URI推测)的intent只有在过滤器URI匹配并且没有指定MIME种类的情况下匹配。

c.只包含MIME种类但是没有包含URI的intent只有当过滤器列举出了一样的的MIME类型并且没有指定URI格式的时候匹配。

d.一个同时指定了URI和MIME类型的intent只有在类型匹配过滤器指定的类型通过MIME类型测试部分,要么URI匹配过滤器指定的URI或者它有一个content:或者file:或者过滤器没有指定URI的情况下通过URI测试。换句话说,如果过滤器列表只有一个MIME类型的话一个成分被假定支持content:和file:数据。

最后一个规则,反映了成分能够从一个文件或者一个内容获得本地数据的期望。所以,他们的过滤器可以列举仅仅一个数据类型并且没必要显式地命名content:和file:机制。这是一个典型的例子。一个下面这样的<data>元素,举例来说,告知安卓成分可以从一个内容提供器获得图片数据并且显示它:

<intent-filter>

<data android:mimeType="image/*" />

...

</intent-filter>

由于大多数合适的数据被内容提供器配送,指定一个数据类型但是没有URI的过滤器可能是最常见的。

另一个常见的配置式带有一个机制和一个数据类型的过滤器。例如,一个想下面这样的<data>元素告知安卓成分为了执行动作可以从网络上取回视频数据:

<intent-filter>

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

...

</intent-filter>

intent匹配

intent和intent过滤器的匹配不仅仅是为了发现一个待激活的目标成分,也式为了发现关于设备上的成分集合的一些信息。例如,Home应用通过使用intent过滤器指定ACTION_MAIN动作和CATEGORY_LAUNCHER类别找到所有的活动来填充应用启动器。

你的应用可以使用intent用类似的方法匹配。PackageManager有一系列的query...()方法来返回所有的接受特定intent的成分。一个类似的系列resolve...()方法决定最佳的响应intent的成分。例如,queryIntentActivities()返回可以执行作为形参的intent所有活动的列表。queryIntentServices()返回类似的服务列表。没有方法是激活成分的;他们只是列举那些可以响应的。有个类似的方法queryBroadcastReceivers(),为广播接收者返回响应列表。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: