Intents和 Intent Filters详解

来源:互联网 发布:用户画像静态数据 编辑:程序博客网 时间:2024/06/05 15:57

Intent是一个消息对象,您可以使用它请求另一个应用程序组件执行特定动作。Intent有三个基本使用场景:
1、 用于启动一个Activity:
您可以通过传递一个Intent到startActivity()startActivityForResult()来启动另一个Activity。Intent用于描述要启动的Activity,及携带一些必需的数据。
2、 用于启动一个Service:
您可以通过传递一个Intent到startService()bindService()来启动或绑定一个Service。Intent用于描述要启动的Service,及携带一些必需的数据。
3、 用于传递一个广播:
你可以通过传递一个Intent到sendBroadcast()sendOrderedBroadcast()来发送广播到其他APP。

Intent的类型

Intent可分为两种类型:
1、 显式Intent :用类名来指定目标组件。通常您会使用显式Intent启动应用内的组件,因为您知道要启动的Activity或Service的类名。例如,APP响应用户点击操作启动一个新的Activity,或者启动一个Service来在后台下载文件。

2、 隐式Intent :不通过类名来指定目标组件,而是指定将要执行的Action,其它APP组件用IntentFilter匹配Intent并做相应处理。例如,如果要在地图上向用户显示位置,您可以使用隐式Intent请求另一个有此功能的APP在地图上显示指定的位置。
这里写图片描述
图1,隐式Intent启动另一个Activity时,是如何在系统中传递的: [1]Activity A用Action创建一个Intent,并把Intent传递给startActivity()。[2]Android系统在所有已安装的APP中查询与这Intent匹配的IntentFilter。当查询到匹配的IntentFilter,[3]系统通过调用onCreate()方法并传入Intent,启动匹配到的Activity B。

图1显示了用隐式Intent启动一个Activity时Intent是如何传递的。如果用显式Intent启动Activity或Service,系统会立刻启动Intent中指定的目标组件。

如果您使用的是隐式Intent,Android系统通过比较Intent和其他APP的IntentFilter来找到将要启动的合适组件。如果Intent与某个IntentFilter匹配,系统会启动此IntentFilter对应的组件并传入Intent。如果Intent与多个IntentFilter匹配,系统会展示一个选择框供用户选择使用哪个APP。

IntentFilter是APP的manifest文件中的一种描述方式,它指定组件希望接收的Intent类型。例如,通过声明一个Activity的IntentFilter,您可以让其他APP以某种特定的Intent直接启动您的Activity。同样,如果您没有声明某Activity的IntentFilter,那么它只能以显式Intent启动。

Intent的结构

Intent对象携带用于Android系统决定启动哪个组件的信息(例如接收此Intent的确切组件名称或组件类别),用于目标组件执行特定Action所需的额外信息(例如需要执行的Action或需要处理的数据)。
Intent中包含的主要信息如下:
Component name
要启动的组件的名称。这是可选的,但它是显式Intent的关键信息,表示Intent应该只传递给Component name指定的应用程序组件。如果Intent没有指定Component name,那么就是隐式Intent,系统基于其他Intent信息(如下面描述的Action、Data和Category)决定哪个组件应该接收此Intent。如果您需启动APP内的特定组件,则应指定Component name。

Intent的这一字段叫ComponentName对象,你可以使用目标组件的完整类名来指定这个字段,例如“com.example.exampleactivity”。 你可以用setcomponent(),setclass(),setclassname()或Intent的构造函数来设置Component name。

Action
指定要执行动作的字符串(如view或pick)。在Broadcast Intent的情况下,代表已发生的并正在广播的动作。Action主要决定了Intent的其余部分是如何构造的,特别是Data和Extra中包含的信息。

您可以自定义Action以供其他APP调用您的组件,您也可以使用由Intent类或其他Framework类定义的Action常量。下面是启动Activity的一些常见Action:
ACTION_VIEW:使用携带此Action的Intent可实现将信息通过Activity展示给用户(如在相册中展示图片,在地图中展示位置信息)。

ACTION_SEND:使用携带此Action的Intent可通过其他APP实现分享功能。

查看Intent类的索引可以了解更多Action常量的定义。还有其他Action常量定义在Framework中,如Settings类中定义了启动系统设置特定界面的Action常量。您可以用setAction()函数,或Intent的构造函数来设置一个Intent的Action。如果您自定义Action,请确保Action以包名为前缀,如:

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

Data
数据的URI和数据的MIME type。数据的类型通常由Intent的Action决定。例如,如果Action是 ACTION_EDIT,那么数据应包含该待编辑文件的URI。

在创建一个Intent时,除了URI之外,还应该指定数据类型(MIME type)。例如,能够显示图片的Activity无法播放音频文件,尽管URI格式可能类似。指定数据的MIME类型有助于Android系统找到最佳组件来接收您的Intent。但是,有时可以从URI推断MIME类型,特别是在数据为content: URI时。content: URI表示数据在设备上,并由ContentProvider控制,这使数据的MIME类型在系统中是可见的。

调用setData()方法设置Data URI,调用setType()方法设置MIME type。或者调用setDataAndType()同时设置Data URI和MIME type。注意:单独调用setData()setType()会分别把MIME type和Data URI置空,如果要同时设置Data URI和MIME type,请使用setDataAndType()

Category
用于设置一些附加信息,表示处理Intent的目标组件的类型。一个Intent可以包含多个Category,但是大多数Intent不需要设置Category。下面是一些常见的Category:
CATEGORY_BROWSABLE: 目标Activity允许由Web浏览器启动,以显示由链接引用的数据,如图像或电子邮件消息。
CATEGORY_LAUNCHER: 该Activity是task的初始Activity,并可从系统桌面点击进入。

可参考Intent类的索引,查看更多Category的定义。可以通过addCategory()方法设置Intent的Category。

上面列出的这些属性(Component name、Action、Data和Category)代表了Intent的特性。通过解析这些属性,Android系统能够判断应该启动哪个APP组件。除开这些属性,一个Intent还可以携带其它信息,而这些属性不用于系统定位目标组件。这些属性为:
Extras
Key-Val格式的键值对信息,用于承载目标组件完成Action所需的数据。可以用putExtra()方法添加一对key和val,也可以把key-val放入bundle中,然后通过putExtras()把bundle插入Intent。例如,用ACTION_SEND创建一个发送email的Intent,你可以用EXTRA_EMAIL key设置邮件地址,用EXTRA_SUBJECT key设置邮件主题。

Intent类中定义了许多EXTRA_*的常量,用于做一些常用的标准数据的key。如果你需要自定义extra keys,请注意用APP的包名做前缀,如:

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

Flags
Flags是在Intent类中定义的,用于指示系统应该如何启动一个Activity(例如,这个Activity应用属于哪个task),和启动这个Activity后应该怎样维护其状态(例如,这个Activity是否要在近期Activity列表里)。

Flags的更多信息,请参考setFlags()。

显式Intent的例子

要创建一个显式Intent,必须设置目标组件的Component name,所有其他属性都是可选的。例如,你的APP中有个名字为DownloadService的Service,用于从Web下载文件,你可以用下面的代码来启动它:

// 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的例子

一个隐式的Intent指定一个Action,它可以调用设备上的能够执行该操作的任何APP。当APP不能执行某个操作,但是其他APP有时,您可以使用隐式Intent来让用户选择使用哪个APP来完成这个操作。例如,你需要实现分享功能,那么可以用ACTION_SEND 来创建一个Intent,并且把分享内容添加到extras。当你用Intent来调用startActivity(),用户就可以选择使用哪个APP来完成分享功能。

注意:当你用隐式Intent来调用startActivity()时,系统可能没有能处理这个Intent的组件,这时你的APP就会crash。你可以用resolveActivity()来校验是否有组件能处理你的Intent,如果返回为null,则没有组件能处理此Intent,那么你应该禁用这个功能,防止发生crash;如果返回非null,则有组件能处理此Intent,可以调用startActivity()启动目标组件。如下例子,先校验Intent,再调用startActivity():

// 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()后,系统会检查所有已安装的APP,查找是否有能处理此Intent(Action为ACTION_SEND,dataType为”text/plain”)的组件。如果只有一个APP能处理此Intent,那么直接调用此APP并传入Intent;如果有多个APP能处理此Intent,那么系统展示一个选择框(如图2),让用户选择启动哪个APP。
这里写图片描述
图2:弹出选择框,让用户选择启动哪个APP

强制弹出APP选择框

如果有多个APP能处理你的Intent,用户可以选择使用哪个APP且设置默认选择项。在某些场景下,设置默认选择项是非常必要的,如用户更习惯每次都用同一个浏览器来打开一个超链接。但是在某些场景下,用户可能每次都需要用不同的APP。如用ACTION_SEND来完成分享动作时,用户会根据实际情况选择不同的APP完成分享功能,所以你必须弹出一个选择框(如图2)让用户选择使用哪个APP来完成分享动作。

要显示选择框,请用createChooser() 来创建Intent,然后把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);}

接收一个隐式Intent

为了表明应用程序可以接收的隐式Intent,请在manifest文件中为对应的应用程序组件声明一个或多个IntentFilter。每个IntentFilter根据Intent的Action、Data和Category指定它接受的Intent类型。只有当Intent通过你的IntentFilter时,系统才会向你的应用程序组件提供一个隐式的Intent。一个显式的Intent总是传递给它的目标组件,不管组件声明的IntentFilter是什么。

应用程序组件应该为它的每一个独立的功能声明独立的IntentFilter。例如,图像库应用程序中的一个Activity可能有两个IntentFilter:一个用于查看图像的IntentFilter,另一个是用于编辑图像的IntentFilter。当Activity启动时,它检查Intent并根据Intent中的信息决定如何行为(例如是否显示编辑器控件)。

每个IntentFilter都是由应用程序的manifest文件中的<intent-filter>元素定义的,嵌套在相应的应用程序组件中(例如<activity>元素)。在< intent-filter >中,可以使用这三个元素中的一个或多个来指定接受的Intent类型:

:声明接受的Intent的Action。值必须是Action的文字字符串值,而不是类常量。
:声明接受的数据类型,使用一个或多个属性指定数据URI(scheme, host, port, path)和MIME类型。
:声明接受的Intent类别。值必须是Category的文字字符串值,而不是类常量。注意:接收隐式Intent,你必须在IntentFilter中包括CATEGORY_DEFAULT的类别。startActivity()startActivityForResult()方法对待所有Intent为CATEGORY_DEFAULT的类别。如果您没有在IntentFilter中声明CATEGORY_DEFAULT,则任何隐式Intent都不能启动你的Activity。

例如,下面是一个IntentFilter,能接受Action为ACTION_SEND,DataType为text的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>

您可以创建一个IntentFilter,该IntentFilter包含多个<action>,<data><category>。如果您这样做,您需要确定组件可以处理这些筛选元素的任意组合。当您想要处理多种Intent时,但仅在Action、Data和Category的特定组合中,则需要创建多个IntentFilter。

通过比较Action、Data和Category这三个元素,对Intent和IntentFilter进行匹配。这三个元素都匹配成功,Intent和IntentFilter才匹配成功。如果其中一个元素匹配失败,系统不会把Intent传递给对应的APP组件。然而,由于一个组件可能有多个IntentFilter,Intent通过某个IntentFilter但可能通过另一个IntentFilter。

IntentFilter的例子

为了演示一些IntentFilter的使用方法,这里是一个社交共享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>

第一个Activity MainActivity,是应用程序的主入口点,当用户点击桌面上此APP的图标启动APP时,会进入MainActivity。
ACTION_MAIN:表明这是程序的主入口点,不需要设置任何的Intent Data。
CATEGORY_LAUNCHER:指示此Activity的图标应放在系统的桌面上。如果<activity>元素未指定图标,则系统将使用<application>元素的图标。
为了使Activity出现在系统的Launcher上,必须同时使用这两个元素。

第二个Activity ShareActivity,用于分享text和media内容,另一个APP可以用与IntentFilter匹配的隐式Intent来启动这个Activity。

使用PendingIntent

PendingIntent是对Intent的封装。一个PendingIntent的主要作用是在满足某些条件时执行其包含的Intent。PendingIntent的常见使用场景:
1、 当用户点击通知栏时执行一个Intent(Android系统的NotificationManager 执行这个Intent)。
2、 当用户点击App Widget时执行一个Intent( Home screen app执行这个Intent)。
3、 在未来的某个时间执行一个Intent(Android系统的AlarmManager 执行这个Intent)。

创建PendingIntent 的方法:
1、 用PendingIntent.getActivity() 从系统取得一个用于启动一个Activity的PendingIntent对象。
2、 用PendingIntent.getService() 从系统取得一个用于启动一个Service的PendingIntent对象。
3、 PendingIntent.getBroadcast()从系统取得一个用于向 BroadcastReceiver广播Intent的PendingIntent对象。

Intent解析

当系统收到一个启动Activity的隐式Intent时,它将根据三个方面将它与IntentFilter进行比较,以寻找处理此Intent的最佳Activity:
1、 Action
2、 Data(URI和DataType)
3、 Category
以下部分描述了如何根据应用程序的manifest文件中的IntentFilter声明将Intent匹配到适当的组件。

Action筛选

IntentFilter可以声明零个或多个元素,如下面的示例所示:

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

要通过IntentFilter的筛选,Intent中指定的Action必须与IntentFilter中列出的Action之一匹配。如果IntentFilter没有列出任何Action,则任何Intent都不能通过筛选。但是,如果一个Intent没有指定一个Action,只要IntentFilter包含至少一个Action,它就会通过筛选。

Category筛选

IntentFilter可以声明零个或多个元素,如下面的示例所示:

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

要通过IntentFilter的筛选,Intent中的每个Category都要与IntentFilter中列出的Category之一匹配。反向则不必要这样,IntentFilter可以声明更多的Category,而不是Intent中指定的,但是Intent仍然可通过筛选。因此,不管IntentFilter中声明了什么Category,没有设置Category的Intent总是通过筛选。

Data筛选

IntentFilter可以声明零个或多个元素,如下面的示例所示:

<intent-filter>    <data android:mimeType="video/mpeg" android:scheme="http" ... />    <data android:mimeType="audio/mpeg" android:scheme="http" ... />    ...</intent-filter>

每个元素都可以指定一个URI结构和一个dataType(MIME media type)。URI的每个部分都是单独的属性:scheme, host, port, 和 path:

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

下面的示例显示了这些属性的可能值:

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

在这个URI中,scheme是content,host是com.example.project,port是200,path是folder/subfolder/etc
这些元素中属性中都是可选的,但是有线性依赖关系:
1、 如果scheme未指定,则忽略host
2、 如果host未指定,则忽略port
3、 如果scheme和host都未指定,则忽略path

当Intent中的URI与IntentFilter中的URI进行比较时,它仅与IntentFilter中包含的URI的部分进行比较。例如:
1、 如果IntentFilter只指定scheme,那么所有有相同scheme的URI都通过筛选。
2、 如果IntentFilter只指定scheme和host,那么所有有相同scheme和host的URI都通过筛选。
3、 如果IntentFilter指定scheme、host和path,那么只有有相同的scheme、host和path的URI能通过筛选。
注意:path规范可以包含通配符星号(*),只需要path名称的部分匹配。

Data筛选将Intent中的URI和MIME类型与IntentFilter中指定的URI和MIME类型进行比较。规则如下:
1、 一个既不包含URI又不包含MIME类型的Intent只有在IntentFilter没有指定URI或MIME类型时,才能通过筛选。
2、 一个包含了URI不包含MIME类型(不管是显式的还是从data推测的)的Intent,只有在URI与IntentFilter的URI匹配且IntentFilter没有设置MIME类型时,才能通过筛选。
3、 一个包含了MIME类型不包含URI的Intent,只有在IntentFilter包含了相同的MIME类型且不包含URI时,才能通过筛选。
4、 一个包含了URI和MIME类型(不管是显式的还是从data推测的)的Intent,如果它的URI与IntentFilter中的URI匹配,或者它有一个content:或file:的URI,而过滤器没有指定一个URI时,才允许URI部分通过筛选(即如果一个组件只指定了MIME类型,那么它支持content:和file:类型data);只有当其MIME类型匹配IntentFilter中列出的MIME类型时,才允许MIME类型部分通过筛选。
最后一条规则,反映了组件能够从file或content provider获取本地数据。因此,这种IntentFilter只列出一种data type,不需要显式地命名content:和file:类型的scheme。下面的示例展示了一个典型的例子,其中元素告诉Android系统本组件可以从content provider获取图像数据并显示它:

<intent-filter>    <data android:mimeType="image/*" />    ...</intent-filter>

指定data type但不是URI的IntentFilter可能是最常见的,因为大多数可用数据都是由content provider分发的。

另一个常见的配置是带有一个scheme和一个data type的IntentFilter。例如,像下面这样的元素告诉Android系统,本组件可以从网络中检索视频数据,以便执行操作:

<intent-filter>    <data android:scheme="http" android:type="video/*" />    ...</intent-filter>

Intent匹配

Intent与IntentFilter匹配,不仅可以发现目标组件,还可以发现设备上的组件集。例如,Launcher通过查找Action为ACTION_MAIN且Category为CATEGORY_LAUNCHER的IntentFilter来发现每个APP在桌面的入口启动组件。只有Action和Category都匹配成功,Intent和IntentFilter才匹配成功。

应用程序可以使用类似于Launcher应用程序的方式进行Intent匹配。PackageManager类有一系列query...()的函数可以返回能处理某个Intent的组件;有一系列resolve...()的函数可以判断哪个组件最适合处理某个Intent。例如,queryintentactivities()返回能够响应某个Intent的所有Activity,queryIntentServices()返回能够响应某个Intent的所有Service,queryBroadcastReceivers()返回能够响应某个Intent的所有Broadcast Receivers。

原创粉丝点击