Intents and Intent Filters全解析

来源:互联网 发布:蓝色降落伞 知乎 编辑:程序博客网 时间:2024/06/15 20:01

官网地址:http://developer.android.com/guide/components/intents-filters.html

一个Intent(意图)是一个消息对象,您可以使用它从另一个应用程序组件(Activity、Service、Content Provider、Broadcast Receiver)启动一个Action。尽管Intent很方便在几个组件之间通信,但它常用的基本使用场景有三个:

  • 启动一个Activity
一个Activity在App中呈现一个简单的界面。你可以通过startActivity()启动一个新的Activity,这个过程可以通过Intent传递数据。
如果你想当Activity finish时获得Activity返回的数据,可以在当前Activity调用startActivityForResult(),然后在onActivityResult()回调方法中处理Intent返回的数据。
  • 启动一个服务
Service在Android中是一个没有用户界面,在后台处理事务的组件。可以通过startService()启动Service来进行一个一次性的操作(比如下载文件),通过intent传递数据。
如果Service作为客户端-服务器的接口,可以通过bindService()来绑定一个Service。
  • 发送一个广播
广播(broadcast)是任何App都可以接收的消息。系统可以发送各种广播事件,比如系统启动、设备开始充电等等。你可以通过sendBroadcast(), sendOrderedBroadcast(),或者 sendStickyBroadcast()发送一个广播,并通过intent发送数据。

Intent类型

有两种类型的Intent:

显式意图(Explicit intents

通过完整的类型启动一个组件。在你的App中,你可以通过显式intent来启动一个组件,因为你知道组件的完整类名。

隐式意图(Implicit intents

隐式intent没有指定要启动的组件,而是指定一个action,这个action可以启动一个另一个app。例如,你想在地图应用中显示用户位置,你可以使用隐式intent来启动地图应用并在地图中显示用户位置。


当你创建一个隐式intent,Android系统会将intent的内容与设备上其他App的manifest文件中声明的intentfilter进行比较,找到适当的组件启动。如果intent匹配一个intentfilter,系统将启动该组件并传递intent对象。如果多个intentfilter都匹配了,系统就会显示一个对话框,用户可以选择使用哪个应用程序。

IntentFilter是一个应用程序的manifest文件中用于接收指定Intent类型表达式的组件。例如,通过在Activity中声明IntentFilter,你可以让其他应用程序通过Intent直接启动你的Activity。同样,如果你不为Activity声明任何IntentFilter,那它可以使用一个显式Intent启动。

注意:为了确保你的应用程序安全,在启动Service时请务必使用显式Intent,并且不要为Service声明任何的IntentFilter。使用隐式Intent启动Service是一种安全风险,因为你不能确定哪个Service将响应Intent,用户也不能看到哪个Service启动了。在Android 5(API Level 21)开始,如果你使用隐式Intent调用bindservice()系统会抛出一个异常。

intent-filters

创建一个Intent

一个Intent对象携带的信息确定哪些组件启动(如确切的组件名称或组件类别(category )),加上通过Intent接受到的信息,以便能正确执行这个action(如设置action和data)。

Intent中包含的最主要信息如下:

Component name

这是可选的,但他决定了一个intent是否是显式intent。没有Component name,intent将会是隐式的,会启动哪个组件将会由其他信息( action, data, 和category,下面会介绍)决定。所以,如果你要显式的启动一个组件,你需要设置Component name。
注意:启动一个Service,你应该总是指定一个Component name来显式启动它。否则,你将不确定哪个Service会响应,用户看不到哪个Service启动了。
Intent中的Component name字段,是ComponentName对象。你可以用目标组件的完整类名(包括包名)来指定他。例如com.example.ExampleActivity。你可以通过setComponent(), setClass(),setClassName()或者intent的构造方法来设置Companent name。

Action

一个字符串,指定特定的action去执行。你可以为你的app指定自己的actions(或用于在你的app中启动其他的app)。但是启动其他app,最好用action常量(Intent里的常量或其他Framework类的常量),下面是一些例子:

ACTION_VIEW

使用这个action来startActivity(),一般用于将一个照片显示在Gallery中或者在地图中显示位置

ACTION_SEND

使用这个action来startActivity(),可以实现分享功能。

可以查看Intent的官方API,里面定义了很多的常量,还有一些常量定义在framework里,比如Settings这个类,里面的常量action用于启动系统的设置界面。
你可以通过setAction()方法或intent的构造方法来设置action。


Data

URI(Uri对象)指定数据的MIME类型,例如,指定一个action为ACTION_EDIT时,则Data需要包含编辑文档的URI。
当创建一个intent,经常需要指定Data的类型(MIME类型)作为他的URI。比如,尽管URI格式很相近,一个Activity可能只能够显示图片但不能播放音频文件。所以,指定Data的MIME类型可以帮助Android系统找到最合适的组件去执行这个意图。然而,MIME类型有时可能以特殊的content:开头,它代表data受设备中ContentProvider控制的,所以在整个系统中都是可见的。
仅设置data的URI,使用setData();仅设置MIME类型,调用setType(),如果需要的话,可以一起设置setDataAndType()
注意:如果你要同时设置URI和MIME类型,不要setData()和setType()都调用,因为这两个方法都会让对方的设置无效。正确的做法是setDataAndType()

Category

一个字符串,含有组件处理intent的附加的信息。intent中可以设置任意数量的category,但大多数的intent不需要category。下面是一些常用的Category:

CATEGORY_BROWSABLE

表示启动的Activity可以由浏览器打开并显示超链接上的数据(比如图片或email信息)

CATEGORY_LAUNCHER

表示activity是App启动的第一个Activity

你可以通过addCategory()方法指定一个Category。

上面列出的这些属性(component name, action, data, 和 category)定义了Intent的特征。通过读取这些属性,Android系统能够知道启动哪个组件。
然而,intent还可以附带一些信息,这些信息不会影响组件的特征。intent可以提供:


Extras

Key-Value的键值对形式,有些action使用URI,有些action使用Extras来传递数据。
你可以使用各种重载的putExtra()方法来添加Extras,你还可以创建一个Bundle来存储Extras的数据,使用putExtras将其设置进去。
例如,你创建一个ACTION_SEND的intent来发送邮件,你可以用EXTRA_EMAIL作为key指定收件人,用EXTRA_SUBJECT作为key指定主题。
Intent对象指定了很多EXTRA_*的常量。如果你需要声明自己的Extra key,确保他包含了你的app的包名,例如static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";

Flags

Flags可以引导系统如何启动一个Activity(例如Activity从属于哪一个task)以及启动后如何对待它(比如它是否存在于最近的Activities)。


显式Intent的例子


比如你创建了一个名为 DownloadService的Service,用于从网络下载文件,你可以使用下面的代码来启动它。
// 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参数和Class参数,因此,这个显式intent在app中启动了DownloadService

隐式Intent的例子

隐式Intent指定一个action来执行设备上的任意的符合这个action的app。当你想你的app不能执行这个action,但是其他的app又可以执行,而且你想用户在执行action需要选择启动哪个app时,使用隐式intent会非常有帮助。
注意:有可能你调用startActivity()发出的隐式Intent不能启动用户的任何一个app,如果发生了这个事,这个调用就失败了而且你的app会crash掉。为确保一个activity会收到intent,调用intent对象的resolveActivity()。如果返回非null,说明至少有一个app可以启动,这时可以安全的调用startActivity()。如果返回结果是null,你就不能使用这个intent,如果可能的话,你应该放弃这个intent。

// Create the text message with a stringIntent sendIntent = new Intent();sendIntent.setAction(Intent.ACTION_SEND);sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);sendIntent.setType("text/plain");// Verify that the intent will resolve to an activityif (sendIntent.resolveActivity(getPackageManager()) != null) {    startActivity(sendIntent);}

调用startactivity(),系统检查所有的安装的应用程序,以确定哪些可以处理这种意图(Intent的ACTION_SEND的action,处理“text/plain”数据)。如果只有一个应用程序可以处理它,该应用程序立即打开。如果多个Activity可以处理这个意图,系统则显示一个对话框,让用户可以选择使用哪个应用程序。

强制使用App选择框

当有一个以上的应用程序,响应你的隐式意图,用户可以选择其中一个应用程序启动,并将该应用程序设为默认选择。因此从现在开始,当执行一个action时有多个app响应时,用户可以选择同一个app进行处理而不用选择,这是很好的体验。比如打开一个网页时,用户往往只是希望用一个Web浏览器打开。

然而,如果多个应用程序可以响应这个意图并且用户可能希望每次都使用不同的应用程序,你应该明确地显示一个选择器对话框。选择对话框要求用户每次都要选择启动哪一个app用户不能选择action的默认程序)。例如,当你的应用程序使用ACTION_SEND 的action进行分享操作时,用户可能想根据当前的情况使用不同的App进行分享,所以你应该总是使用选择对话框。如右图:
intent-chooser
要显示选择框,使用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 dialogIntent chooser = Intent.createChooser(sendIntent, title);// Verify the original intent will resolve to at least one activityif (sendIntent.resolveActivity(getPackageManager()) != null) {    startActivity(chooser);}
这个例子用createChooser()方式创建intent,并提供了一个chooser_title作为对话框的标题,对话框中将显示多个app供用户选择。


接收一个隐式Intent

为让你的app能接收一个隐式Intent,需要在manifest文件中你的app节点下声明一个或多个 <intent-filter>元素。每一个intent-filter中基于action, data和category指定了可以接受的intent类型。当intent满足intent-filter的条件时系统将发送一个隐式intent给你的app组件。

注意:一个显示Intent,总是能准确的找到目标组件,无视Intent-Filters的声明
一个应用程序组件应该为每一个单独的工作声明独立的过滤器。例如,在一个图像库App中一个Activity可具有两个过滤器:一个过滤器用来查看的图像,另一个过滤器来编辑图像。当Activity启动时,它检查intent,并基于在Intent上的信息(诸如,是否显示编辑器控制)决定如何响应。
在manifest文件中每个intent filter 由一个<intent-filter>元素定义,并嵌套在对应的App组件(如一个<Activity>元素)中。在<intent-filter>中,你可以使用这三个元素(action,data,category)的一种或多种来指定Intent的类型

例如,这里是一个Activity的声明,里面嵌入一个ACTION_SEND的intent-filter:
<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-filter中定义的多个intent。所以,要响应多个intent,创建多个action,data,category即可。

对于所有的Activity,你必须在manifest文件中声明intent-filter。然而,对于broadcast receiver可以调用registerReceiver()进行动态注册,然后你可以调用unregisterReceiver()进行解注册。这样做可以让你的app在一个特定的时间运行app时监听特定的广播。

过滤器的例子

为了更好的理解intent-filter的行为,下面是一个社交分享app的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中ACTION_MAIN的action表示是程序入口。CATEGORY_LAUNCHER 的category表示Activity的图标会显示在系统桌面上。如果<activity>元素中没有指定icon属性,系统会使用<application>元素中的icon属性。

注意application/vnd.google.panorama360+jpg这种MIME类型,是一个特殊的data类型,他表示你可以使用google全景API( Google panorama APIs)来处理全景照片。

使用Pending Intent

PendinIntent对象是对Intent对象进行了封装。主要作用是给Intent权限可以启动外部应用,就好像是从自己的应用进程执行的一样。
pending intent的主要使用场景包括:
声明一个intent在未来某个时候当用户点击了Notification才被执行(NotificationManager执行Intent)
声明一个intent在未来某个时候用户操作App Widget再执行(桌面屏幕app执行Intent)
声明一个intent在未来某个特定时间执行(Android系统的AlarmManager执行Intent)
由于每个声明的intent是用来处理某个特定的组件(Activity, Service, 或BroadcastReceiver),所以创建PendingIntnet也要基于一样的考虑。当使用PendingIntent,你的app不会通过调用startActivity()马上执行。当你创建PengdingIntent你必须声明即将要执行的组件:
PendingIntent.getActivity()创建 启动Activity的Intent.
PendingIntent.getService() 创建  启动Service的Intent.
PendingIntent.getBroadcast() 创建 启动 BroadcastReceiver的Intent.

Intent解决方案

当系统收到一个隐式intent启动一个Activity,它会通过intent的action、data(URI和data类型)、category搜索比较找到最合适的Activity来启动。
接下来的部分将描述对于manifest文件中声明的intent-filter而言intent是如何匹配到最合适的组件的。

action测试

指定可以接收到的action,可以声明0个1个或多个action,如:
<intent-filter>    <action android:name="android.intent.action.EDIT" />    <action android:name="android.intent.action.VIEW" />    ...</intent-filter>


Category测试

指定可以接收到的category,可以声明0个1个或多个category,如
<intent-filter>    <category android:name="android.intent.category.DEFAULT" />    <category android:name="android.intent.category.BROWSABLE" />    ...</intent-filter>

Data测试

指定可以接收到的data,可以声明0个1个或多个data,如
<intent-filter>    <data android:mimeType="video/mpeg" android:scheme="http" ... />    <data android:mimeType="audio/mpeg" android:scheme="http" ... />    ...</intent-filter>

Each <data> element can specify a URI structure and a data type (MIME media type). There are separate attributes — schemehostport, and path — for each part of the URI:

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

例如:
content://com.example.project:200/folder/subfolder/etc
这些属性在<data>元素中都是可选的,但是他们存在线性依赖性:
如果没有指定schema,host被忽略
如果没有指定host,port被忽略
如果schema和host都没指定,path被忽略。

intent匹配

intent不仅会激活启动匹配到的组件,而且会找到设备中所有附和条件的组件。比如,系统桌面应用app就找到了在intent-filter中指定ACTION_MAIN的action和CATEGORY_LAUNCHER的category的所有Activity。
你的应用可以使用类似的方法进行intent的匹配。PackageManager 有一系列的query...()方法,它会返回所有接收特殊intent的组件,类似的,还有一系列resolve...()方法,用于决定哪一个组件最适合来响应intent,例如queryIntentActivities()通过参数返回一个activity的集合,同样queryIntentServices()返回service的集合。这几个方法都不会激活组件,他们只是列出他们可以响应的组件。对于broadcast receivers,类似的有queryBroadcastReceivers()方法。


介绍到此,更多详细内容请参考Android官网:http://developer.android.com/guide/components/intents-filters.html




0 0