intent和intent filter-Android 6.0开发者文档

原文地址:Intents and Intent Filters

一、前言

intent是用来表示对其他应用组件的动作的信息载体。intent有很多种用法,可以促进组件之间的通信,其中三个基本用法如下:

  1. 启动activity
    一个activity代表了应用的一个界面。你可以通过向startActivity()方法传递intent来启动一个activity实例。这里的intent描述了寻找目标activity需要的数据和其他附带的数据。
    如果你希望activity结束后能收到返回结果,可以调用startActivityForResult()方法。目标activity的结果将通过你的activity的onActivityResult()回调的intent对象传给你。更多信息请看activity部分。

  2. 启动service
    service没有UI,它会在后台进行一些操作。你可以通过向startService()方法传递intent来启动一个service。这里的intent描述了寻找目标service需要的数据和其他附带的数据。
    如果此service被设计为一个CS接口(client-server interface),你可以通过向bindService()方法传递intent来绑定这个服务。更多信息请看service部分。

  3. 发送广播
    广播是所有应用都能收到的消息。系统事件发生后,系统会发送系统广播,例如,当设备开机时会发送开机广播。你可以通过向sendBroadcast()、sendOrderedBroadcast()或sendStickyBroadcast()方法传递intent来发送广播。


二、intent类型

有两种intent:

  1. 显式intent
    显式intent通过名称(严格限制的类名)启动组件。典型使用场景是,你可以通过显式intent启动自己应用的组件,因为你知道你要启动的activity或service的类名。
  2. 隐式intent
    隐式intent不会用名称指定组件,而是用一系列“要求”(这种要求便于其他应用处理)指定。它是通过筛选是否符合“要求”来找到目标组件的。

如果你用显式intent启动activity或service,系统会立刻启动intent中指定的应用组件。

如果用隐式intent启动,那么

  1. activity A新建一个有“action”(要求)描述的intent并传递给startActivity()
  2. Android系统从所有应用的intent filter中寻找符合intent要求的组件
  3. 找到以后系统启动这个activity(Activity B)。如下图1
    图1

创建隐式intent以后,Android系统会查询当前所有应用的manifest文件中的intent filter部分,寻找符合要求的目标组件。如果创建的intent符合了某个intent filter,系统会启动这个组件,并将它传递给intent对象。如果有多个intent filter符合了要求,系统会显示一个对话框,由用户选择使用哪个应用。

intent filter是应用manifest文件的一部分,它指定了组件想要获取的intent类型。如果某个activity宣布了一个intent filter,那么其他应用可以通过某种intent启动这个activity。而如果这个activity没有intent filter,那么它就只能被显式intent启动了。

注意:为了保证应用安全,尽量使用显式intent启动service,不要为你的service声明intent filter。使用隐式intent启动service是危险的,因为你不能确定是哪个service响应了intent,用户也看不到是哪个service启动了。从Android 5.0(API 21)开始,如果你用隐式intent调用bindService(),Android系统会直接抛出异常。


三、创建intent

intent携带的信息可以帮助Android系统确定需要启动哪个组件,这些信息通常也可以表现组件的动作(比如希望目标组件执行的action或希望目标组件处理的数据)
intent中包含的信息有:

  1. 组件名称(component name)
    即要启动的组件的名称。
    组件名称是可选的,但是在显式intent中,它是必须的。组件名称表示intent将要发送到的特定组件。如果没有组件名称,那么这个intent是隐式的,将由系统根据intent中携带的其他信息(即后面说的action、data、category)决定哪个组件能收到它。所以,如果你希望启动自己应用的某个组件,最好指定组件名称。
    注意:启动service时,最好指定组件名称。否则,你不能保证哪个service会收到这个intent,而且用户也看不到哪个service启动了。
    组件名称在intent中的字段名为ComponentName,你可以将它指定为你的目标组件的包括包名的完整类名。例如,com.example.ExampleActivity。你可以通过setComponent()、setClass()、setClassName()方法或intent的构造器指定它。

  2. 动作(action)
    指表示某个特定动作的string。
    在广播的intent中,它表示将要采取的动作或将被广播的动作。action对intent中的其他信息有很大影响。
    在自己的应用中,你可以在intent里添加自定义的action(其他应用调用你的应用组件也可以使用自定义action),但是建议你使用intent类或其他框架类中定义的action。下面是一些常用的用于启动activity的action:

    • ACTION_VIEW
      startActivity()时,在intent中添加这个action,可以向用户显示一些信息,如图片浏览器中展示图片或地图应用中显示地址
    • ACTION_SEND
      启动其他应用的activity时,在intent中添加这个action,可以向其他应用分享数据,如邮箱或社交应用

    在intent类中可以找到更多类似的action定义。有一些action分散定义在Android框架中,例如有一个用于系统设置的action,可以在打开系统设置时打开指定的界面。
    可以通过setAction()方法或intent的构造器设置action。
    如果你希望定义自己的action,请确保string是以你的应用包名开头的,如:
    static final String ACTION_TIMETRAVEL = “com.example.action.TIMETRAVEL”。

  3. 数据(data)
    指要操作的数据的引用URI或数据的MIME类型URI。data的内容通常决定于intent的action部分。例如,如果action为ACTION_EDIT,那么data就应该包括要编辑的文件的URI。
    创建intent时,除了要指定URI,最好也指定数据的类型(即MIME类型)。例如,某个可以显示图片的activity可能不能播放音频,但是音频和图片的URI是类似的,这时,如果你指定了数据的MIME类型,Android系统就可以按照这个类型找到更合适获取你的intent数据的组件。某些情况下,MIME类型可以通过URI推断出来,尤其是数据是一个“content:”URI时,它表明数据在设备上,且由一个ContentProvider控制,而ContentProvider可以告诉系统这个数据的MIME类型。
    如果只设置URI,可以使用setData()方法。如果只设置MIME类型,可以使用setType()方法。如果两个都设置,可以使用setDataAndType()方法。

    注意:如果URI和MIME类型你都要指定,那么不要分别调用setData()和setType(),因为这两个方法都会覆盖对方。所以使用setDataAndType()吧

  4. 类别(category)
    是包含了能表示应该处理此intent的组件的类型信息的一个string。intent可以使用很多的category,但是大部分intent都不需要category。一些常用的category如下:

    • CATEGORY_BROWSABLE
      表示目标activity允许被web浏览器启动,并显示链接指定的信息,如图片或邮件信息
    • CATEGORY_LAUNCHER
      表示这个activity是activity栈的入口activity,并且会显示在系统的应用launcher中

    其他category的描述可以在intent类中找到。
    你可以通过addCategory()方法指定category。
    上述的属性描述了intent最典型的特征,通过读取这些属性,Android系统可以确定它需要启动哪个组件。
    intent也可以携带一些不会影响组件选择的信息,如:

    • Extras
      包含了action指定的需要处理的数据,以键值对形式提供。有些action使用数据URI指定需要处理的数据,有些action则是使用extras
      你可以用putExtra()方法添加extra,此方法有两个参数:键的名称和值。你也可以新建一个Bundle对象,在此对象中添加extra信息,再把Bundle用putExtras()方法添加到intent中。
      例如,当你使用action为ACTION_SEND的intent去发送邮件时,你可以用EXTRA_EMAIL这个键指定要发给谁,用EXTRA_SUBJECT这个键指定正文
      intent类中定义了很多标准的数据类型,并用EXTRA_*的形式表示。如果你想要声明自己的extra键(用于给你自己的组件发送intent),请确保此键包含应用名称作为前缀。例如:
      static final String EXTRA_GIGAWATTS = “com.example.EXTRA_GIGAWATTS”;
    • Flags
      flag是intent的固有属性。它可以告诉Android系统怎样启动activity(如,被启动的activity将处于哪个activity栈),还有在它启动后怎样处理它(如,它是否出现在recent activities列表中)
      更多信息请看setFlags()方法

3.1 显式intent示例

显式intent用来启动指定的应用组件,例如你的应用中的activity和service。创建显式intent需要添加组件名称(component name),其他的intent属性不是必须的。

下面举一个显式intent例子:你在应用中定义了一个service,名称为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就显式的启动了应用中的DownloadService。

3.2 隐式intent示例

隐式intent指定了action,并且可能调起设备上任何符合此action的组件。当你的应用不能处理某个action,而其他应用能处理此action,且你希望用户选择哪个应用处理它时,可以使用隐式intent。

例如,你有个信息想要用户分享给其他人,可以使用ACTION_SEND并添加extras(包含你想要分享的信息)到intent中,然后调用startActivity(),将由用户选择将信息分享到哪个应用。

注意:如果在你用隐式intent启动startActivity()后没有找到能处理它的应用,那么此次调用会失败,且你的应用会崩溃。为了确保可以有activity收到这个intent,你可以调用resolveActivity()方法,如果返回值不为空,那么就意味着至少有一个应用可以处理你的intent,这时就可以安全的调用startActivity()了。如果resolveActivity()返回空,建议你不要使用隐式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("text/plain");
// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(sendIntent);
}

注意:在上面的例子中没有使用URI,而是添加了一个extra
调用startActivity()后,系统会检查所有已安装应用,并选择一个应用处理此intent。如果只有一个应用可以处理它,那个应用会立刻获得这个intent。如果有多个应用可以处理它,系统会显示一个对话框让用户选择把intent给谁。

3.3 强制弹出应用选择框

如果有多个应用可以处理隐式intent,用户将会选择其中一个应用处理它并且此应用将成为处理此intent的action的默认应用。这个功能在用户希望用相同应用处理一类请求时很有用,比如打开链接(用户通常有一个优先使用的浏览器)。

然而,也有可能用户希望每次使用不同的应用处理这些action,此时你应该显示一个选择对话框。选择对话框要求用户每次都为此action选择一个目标应用(用户将不能设置默认应用)。例如,如果你的应用使用action为ACTION_SEND的支持分享功能的intent,用户会希望把同样的信息分享到不同的平台,这时你就应该总是显示选择框(当然前提是有分享功能的应用有多个)。

要显示应用选择框,可以使用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 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);
}

上述代码会显示一个列举了能处理intent的应用列表,并使用提供的text作为对话框的标题。


四、接收隐式intent

你可以通过在manifest文件中为组件添加<intent-filter>来告诉系统你能处理哪些隐式intent。intent filter通过action、data、category指定它能接收的intent类型。当某个intent能通过你的intent filter时,系统会将此隐式intent传递给你的组件。

注意:显式intent总是能发送到目标组件,不管这个组件定义了什么intent filter
应用组件应该说明一些filter,以告诉系统它可以做的事情。例如,图片浏览器的activity可以有两个filter:一个查看图片,另一个编辑图片。当此activity启动时,解析intent并决定根据intent中的信息做什么事情。

intent filter有以下元素:

  • <action>
    action的name属性描述可以接受的intent的action名称。这个值必须是一个字符串(不能用类常量定义)
  • <data>
    描述可以接受的数据类型,可以用多个属性描述URI的各个方面(scheme、host、port、path等),也可以描述MIME类型
  • <category>
    category的name属性描述可以接受的intent类别。这个值必须是一个字符串(不能用类常量定义)

注意:为了收到隐式intent,你必须在intent filter中添加CATEGORY_DEFAULT。因为startActivity()和startActivityForResult()是默认所有的intent都有CATEGORY_DEFAULT的。如果你没有在intent filter中添加这个category,你的activity将收不到任何隐式intent。

下面是一个接收action为ACTION_SEND、数据类型为text的intent的activity的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>

一个intent filter可以有多个<action><data><category>。如果你这样做了,请确保你的组件能处理filter中说明的内容。

如果你想要处理符合不同过滤条件的多个intent,可以声明多个intent filter。

隐式intent在通过filter时会比较action、data、category的内容。intent必须通过了这些测试才可以被发送给目标组件。哪怕是其中一个测试失败了,Android系统都会拒绝将此intent发送到目标组件。然而,由于组件可以有多个intent filter,而可能intent可以通过这个又不能通过那个,这种情况下系统分发intent的策略详见intent resolution部分。

使用intent filter不能完全防止其他应用启动你的组件。即使用intent filter指定只回答有限的一些隐式intent,其他应用也可以通过显式intent启动你的组件。如果你想要保证只有自己可以启动自己的组件,可以设置exported属性为false。

警告:为了避免误启动其他应用的service,请使用显式intent启动自己的service,并且不要为自己的service声明intent filter

注意:建议你在manifest文件中为所有的activity声明intent filter。对于broadcast receiver,由于可以通过registerReceiver()、unregisterReceiver()动态注册广播,你可以在应用运行时自定义在某个时间端监听广播。

4.1 filter实例

为了更好的理解intent filter的行为,下面给出一个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是应用的入口,当用户点击桌面的图标时会打开这个activity。

ACTION_MAIN 表示这是一个入口,此入口不希望有任何intent数据
CATEGORY_LAUNCHER 表示这个activity的图标会出现在系统的桌面上。如果这个<activity>没有指定icon属性,系统会默认使用<application>指定的图标
activity必须声明了ACTION_MAIN和CATEGORY_LAUNCHER才能显示在系统桌面上

第二个activity:ShareActivity声明它有分享text和多媒体信息的能力。用户可以在本应用通过MainActivity进入到这个activity中,也可以在其他应用通过发送符合两个filter任意一个的intent进入到这个activity中。

注意:示例中的一个MIME类型-application/vnd.google.panorama360+jpy是用来指定全景图片的特殊数据类型,更多信息请看Google panorama API


五、使用Pending Intent

PendingIntent类是Intent的包装类。PendingIntent的设计目标是给外部应用使用它包含的Intent的权限,就好像这个intent是在自己应用自己进程中执行的。

pending intent的使用场景如下:

  • 当用户点击通知栏时执行的intent(Android系统的NotificationManager执行了这个intent)
  • 当用户点击你的应用小工具时执行的intent(桌面应用执行此intent)
  • 描述一个在未来某个时间执行的intent(Android系统的AlarmManager执行此intent)

与每个intent都对应一个应用组件(可能是activity、service、BroadcastReceiver)一样,PendingIntent也对应了一个应用组件。使用pending intent时,你不能用平常的方法如startActivity()执行它,所以需要用另外的方式将它与组件类型对应::

  • PendingIntent.getActivity()返回一个启动activity的pending intent
  • PendingIntent.getService()返回一个启动service的pending intent
  • PendingIntent.getBroadcast()返回一个启动BroadcastReceiver的pending intent

上述每个方法都需要当前应用的context、你希望包装的intent、一个或多个指定此intent将被如何使用的flag(如是否这个intent可以被使用多次)作为参数。

更多的信息请看notification或app widget API部分。


六、intent分析

系统收到启动activity的隐式intent时,会根据intent filter筛选最好的activity,筛选条件有三个:action、data(URI或数据类型)、category。

下面将介绍intent怎样与组件匹配,和如何在manifest文件中声明intent filter。

6.1 action测试

intent filter可以声明零个或多个<action>,如:

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

为了通过这个filter,intent中的action必须符合filter所列action中的一个。

如果filter没有列任何的action,将不会有任何的intent可以匹配到,所以所有的intent都不能通过此filter。如果intent没有指定action,那么只要filter包含至少一个action,它就可以通过此filter。

6.2 category测试

intent filter可以声明零个或多个<category>,如:

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

intent的每个category都跟filter中的category符合才能通过category测试。另外,如果intent filter声明的category比intent多,只要intent所有category与intent filter都符合,一样可以通过测试。因此,没有category的intent总是可以通过这项测试,而不管filter设置了哪些category。

注意:Android系统会自动为startActivity()或startActivityForResult()的隐式intent添加CATEGORY_DEFAULT。所以如果你想要你的activity可以接受隐式intent,必须将“android.intent.category.DEFAULT”添加到activity的intent filter中

6.3 data测试

intent filter可以声明零个或多个<data>,如:

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

每个<data>都表示一个URI或者数据类型(MIME类型)。它可以为URI添加一些属性:scheme、host、port、path,格式为<scheme>://<host>:<port>/<path>。下面是一个示例:

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

在这个示例中,scheme为content,host为com.example.project,port为200,path为/folder/subfolder/etc。

<data>的每个属性都是可选的,但是它们有一种线性依赖机制:

  • 如果没有指定scheme,host会被忽略
  • 如果没有指定host,port会被忽略
  • 如果scheme和host都没有指定,path会被忽略

当比较intent中的URI和filter中的URI时,仅比较filter包含的URI部分。下面举例说明:

  • 如果filter仅指定了scheme,所有符合此scheme的URI将会通过此filter
  • 如果filter指定了scheme和authority但是未指定path,所有符合scheme和authority的URI将通过此filter,不管这些URI的path是什么
  • 如果filter指定了scheme、authority和path,那么只有scheme、authority、path都符合的URI会通过这个filter

注意:path可以使用通配符:*

data测试既比较URI又比较MIME。比较规则如下:

  1. 如果filter没有指定任何URI和MIME,那么intent的URI测试和MIME测试都不会通过
  2. 如果intent的URI匹配了filter的URI,但是filter没有指定MIME,那么intent会通过URI测试,但不会通过MIMIE测试
  3. 如果intent的MIME匹配了filter的MIME,但是filter没有指定URI,那么intent会通过MIMIE测试,但不会通过URI测试
  4. URI和MIME都包含的intent,会在MIME符合filter的MIME列表时通过MIME测试,在URI符合filter的URI,或,有content:或file:并且filter没有指定URI时通过URI测试。换句话说,如果组件的filter仅指定了MIME类型,会默认认为它支持content:和file:数据

最后一条规则,对组件从文件或content provider获取本地数据放松了限制。因此它们的filter可以仅添加MIME,而不需要明确的说明data为content:或file: scheme。下面是一个典型例子,通过<data>告诉Android系统此组件可以从content provider获取图片数据并显示:

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

由于大部分可用数据是由content provider提供的,所以添加指定数据类型(MIME)但是不添加URI的filter很普遍。

另外一个常用的filter模式是使用scheme和数据类型。下面是一个典型例子,其通过<data>告诉Android系统此组件可以从网络获取video数据:

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

6.4 intent匹配

通过filter的intent可能不仅仅是一个组件,而是多个组件。例如,桌面应用可以通过添加action为ACTION_MAIN、category为CATEGORY_LAUNCHER的filter获取所有的launcher activity。

你的应用也可以这样做。PackageManager有一些query方法,可以返回符合指定intent的所有组件,有一些resolve方法,可以返回符合intent的最佳组件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值