Intents and Intent Filters

http://developer.android.com/guide/components/intents-filters.html#PendingIntent
Intent 是一个messaging对象,可以用于从另一个组件请求一个action。Intent 在组件之间有很多种方便的通信方式,有三种基本的使用方式。

  • 启动一个activity
    一个App中,一个activity表示一屏。可以通过传递一个Intent给startActivity()来实例化一个Activity。Intent描述了activity启动和执行时的必要数据。
    如果你想在activity结束时接受它的结果,可以调用startActivityForResult()。你的activity在onActivityResult()回调中接收这个结果,这个结果是一个独立的Intent对象。

  • 启动一个service
    Service 是一个没有UI在后台执行的组件。可以传递一个Intent给startService()启动一个Service执行一次性操作(比如说下载文件)。Intent描述了Service启动和执行时的必要数据。
    如果Service设计为C/S接口,可以在另一个组件中传递Intent给bindService()来绑定这个Service.

  • 发布一个broadcast
    broadcast是任何APP可以接受的消息。系统为系统事件发送各种广播。比如说,系统启动或者设备开始充电。可以通过传递Intent给sendBroadcast()或者sendOrderBroadcast()或者sendStickyBroadcast()来发送广播给其他APP。

Intent Types

两种Intent类型:

  • 显式Intent
    通过类名启动指定的组件。通常在自己的APP中显式启动组件,因为自己知道要启动的activity或service的类名。比如说启动一个activity来响应一个用户操作或者启动一个service在后台下载文件。
  • 隐式Intent
    不需要指定组件的名字,取而代之的是声明允许其他APP操作的动作。比如说你想在地图中展示用户的位置,就可以用隐式Intent来请求另一个有能力展示在地图中显示用户位置的APP来启动展示。

当用显示Intent来启动activity或service时,系统立即启动指定的APP组件。
当创建一个隐式的Intent时,Android系统通过比较Intent的内容和设备中其他APP的manifest中声明的intent filters来启动一个合适的组件。如果一个Intent和一个intent filter匹配,系统启动这个组件,并将intent对象传给它。如果多个intent filter匹配,系统展示一个对话框列表供用户自己选择。
在一个App的manifest文件中,intent filer是表示组件想要接收哪种类型的intent。举例来说,通过给一个activity声明一个intent filter来使得其他APP使用特定类型的intent来直接启动你的activity。同样的,如果你不为你的activity声明任何intent filter,那只能用显示的intent启动。

Caution:为保证你的APP安全,在启动service时总是显示intent启动,不要为你的service的声明intent filter。使用隐式intent启动service是非常不安全的,因为你不能确定哪个服务响应intent,用户不能看到哪个service启动了。从Android5.0(API21)开始,如果你调用bindService()绑定隐式的intent,系统将会抛出异常。

Building an Intent

一个Intent对象携带了android系统用于判断哪个组件应该启动的信息(比如说精确的组件名称或者应该接收的intent的组件category)和接收组件用于执行操作的信息。(简单说intent有两部分数据:一是用于选择哪个组件启动,一个是传给组件要使用的数据)

Intent包含以下一些信息:

Component name: 启动组件的名字
这是可选的,但这是使intent成为显式的关键信息,意味着定义了组件名称的intent只传递给本APP的组件。没有组件名称,intent是隐式的,系统根据intent的其他信息(比如说 action,category)决定哪个组件接收intent。所以,如果你需要启动你APP中的特定组件,你应该指定组件的名称。
Note: 当启动Service时,你应该总是指定组件名称

Intent的这个域是ComponentName对象,你可以使用目标组件的完全限定域名,包括APP的包名。比如说,com.example.ExampleActivity。你可以用setComponent()、setClass()、或者setClassName(),或者在Intent的构造函数中设置组件名。

Action
一个字符串,指定执行的操作。
在broadcast intent的例子中,就是这个action发生被报告。action很大程度上决定了剩余intent的结构,尤其是是否包含data和extras。
你可以在你自己的APP中定义自己的actions来使用(其他APP可以通过它来唤醒你APP中的组件),但是你应该通常使用Intent类或者框架类定义的action常量。这有很多启动activity的通用action。

ACTION_VIEW
当你有些信息需要在一个activity中展示给用户时,比如说在画廊APP中展示相片或者在地图的APP中展示位置,就可以使用这个action。
ACTION_SEND
也称为“share”Intent,当要分享一些数据给其他APP使用,比如Email或者社交分享App。
可以用setAction()或者在Intent的构造函数中指定action。
如果你定义自己的action,确定包含你的APP的包名做前缀。举例:

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

Data
Data有两部分,URI 表示data要执行什么,MIME表示数据的多媒体类型。数据支持的类型通常被intent的action所规定。比如说:如果action是ACTION_EDIT,data应该包含文档的URI来编辑。
当创建一个intent,除了URI,指定data的MIME类型也很重要。比如说,一个activity可以显示图片但不能播放音频文件,而URI格式很相似,所以data的MIME类型可以帮助android 系统找到接收intent最好的组件。然而,MIME类型有时可以从URI推断出来,尤其是当data是content:的URI时,系统推断数据在本设备上,被ContentProvider控制,使得数据的MIME类型对系统可见。
如果只设置data的URI的话,调用setData(),只设置MIME类型的话,调用setType()。如果需要,可以都显式设置,使用setDataAndType()。

Caution 如果想要设置URI和MIME,不要分别调用setData()和setType(),因为他们会覆盖。使用setDataAndType()设置URI和MIME。

Category

一个字符串,包含能够处理intent的组件种类的信息。任何种类的category描述都可以放到intent中,但是大部分intent不要求category。下面是一些常用的category。

CATEGORY_BROWSABLE
目标activity允许它被一个web浏览器启动来显示数据。比如说一张图片或者一封email信息。
CATEGORY_LAUNCHER
这个activity是一个任务的初始activity,被列在系统应用程序启动列表里。
你可以用addCategory()来指定一个category。
上面列的这些表示intent的特征(组件名称、action、data、category)。系统通过这些特征来判断哪个APP组件应该启动。
而且,一个intent还可以携带不影响判断启动哪个组件的信息。比如说下面这些:
Extras
键值对携带一些用于完成action所需要的信息。就像一些action需要用一些特定类型的URIdata,一些action也需要一些额外的信息。
可以使用各种putExtra()方法来添加extra数据,每一个接收两个参数,一个是键名,一个是值。你也可以创建一个bundle对象来存额外信息,最后使用putExtras()将bundle对象放入intent中。
比如说,当用ACTION_SEND创建一个intent发送email时,你可以使用EXTRA_EMAIL这个键来指定收件人,用EXTRA_SUBJECT键来指定主题。
Intent类中为很多标准化的数据类型设置了EXTRA_常量。如果你需要声明自己的extra键(为了让你的APP接收到)的话,确保将你的APP的包名作为前缀。比如说:

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

Flags
Intent类中定义的Flags作为intent的元数据。flags指导系统怎样启动activity(比如说,activity应该属于哪个task)和在启动之后如何处理它(比如说,它是否在最近的activity的列表中)。
更多详细的信息,可以查看setFlags()方法。

显式intent示例

显式intent用于启动指定的APP组件,比如自己APP中的一个activity或者一个service。使用显式intent时,指定组件的名字,其它的intent属性都是可选的。
比如说,你在你的APP中建立一个名称为DownloadService的service,用于从web下载文件,你可以用下面的代码启动它:

    //Executed in an Activity,so 'this' is the Context
    //The fileUrl is a string URL,such as "heep://www.example.com.image.png"
    Intent downloadIntent = new Intent(this,DownloadService.class);
    downloadIntent.setData(Uri.parse(fileUrl));
    startService(downloadIntent);

Intent(Context,Class)构造函数中,一个是APP的context,一个是组件的类。这样,intent显式的启动了APP中的DownloadService类。
关于创建和启动service的更多信息,可以参考Service Guide。

隐式Intent示例

隐式Intent指定一个action来唤起设备中能够执行这个action的APP。当你的APP中不能执行某个action时,而其他APP可以执行时而且你想让用户选择使用某个APP来执行时,使用隐式intent是非常有用的。
比如说:用户想要分享内容给其他用户,创建一个action为ACTION_SEND的intent,添加上想要分享的内容。当调用startActivity()时,用户可以选择用哪个APP来共享内容。
Caution: 有一种可能,用户没有任何APP来处理用startActivity发送的隐式intent。如果这样的话,调用将失败,而且你的APP将crash。为了解决这个问题,先用intent对象调用resolveActivity()来验证是否有activity在接收你的intent。如果结果不是null,那至少有一个APP可以处理你的intent,那么调用startActivity是安全的。如果结果是null,你不应该使用这个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("text/plain");

    //Verify that the intent will resolve to an activity
    if(sendIntent.resolveActivity(getPackageManager()!=null)){
    startActivity(sendIntent);
    }

Note: 在这个例子中,没有使用URI,但是通过指定data的类型指定了携带内容信息的类型。
当startActivity()调用后,系统检查安装的所有APP来决定哪个来处理这种intent。如果只有一个APP能够处理的话,这个APP立马启动,如果多个activity可以接受这个intent的话,系统会列出一个对话框供用户自己选择。

强制选择一个APP

当不止一个APP可以响应你的隐式intent时,用户可以选择一个APP来使用,并且使这个APP作为这个action的默认选择。执行一个action,用户可能想要一直使用这个app来执行这个action时,这是非常好,比如说打开一个网页(用户通常只会用一个浏览器)。
然而,如果多个APP可以响应intent,并且用户每次想要使用不同的APP来响应,你应该显式的给一个选择框。这个选择框来询问用户每次用哪个APP来响应这个action(用户不为这个action选择默认的APP)。比如说,当你的APP使用ACTION_SEND执行分享时,用户可能会根据不同的情况选择不同的app来执行,所以你应该总是使用选择对话框。如图2所示。
为了显示一个选择器,使用createChoose()来创建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

为了告诉你的APP可以接受什么隐式的intent,在你的manifest文件中使用为你的每个组件声明一个或多个intent filter。每个intent filter根据intent的action、data、category来判定可以接收的intent。只要intent可以通过你的一个intentfilter,系统就会发送一个隐式的intent给你的APP组件。
Note: 即使有组件的过滤声明,显式intent也是发送给它的目标。
一个APP的组件应该为它能完成的每个工作声明独立的滤波器。比如说:一个画廊的APP中的activity可能有两个滤波器:一个用来看image,另一个用来编辑image。当这个activity启动时,它会检查这个intent,然后根据这个intent的信息来决定它的行为。(比如说是否展示编辑功能)
在APP的manifest文件中,用来定义intent滤波器,嵌在相应的组件中(比如说一个元素中)。在里面,你可以使用一个或者多个参数(下面三种元素)来指定可以接收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 filter是可以的。如果这样的话,你需要确定这个组件可以处理这些滤波元素的一些或者全部的组合。
当你想要处理多种intent时,但action、data、category只有一种组合,那么你需要创建多种intent filter。
隐式intent要与intentfilter的三个元素进行比较,要想启动组件,intent必须通过三个元素的测试。如果它不能匹配其中的一个即失败,系统不会将这个intent传给组件。因为一个组件可能有多个intentfilter,一个intent通不过这个filter还可能通过另一个filter。更多关于系统怎样处理intent的信息将在下面Intent Resolution部分给出。

Caution: 为了避免无意的运行不同APP的service,总是使用显式的intent来启动自己的service,不为自己的service声明intent filter。

Note:对于所有的activity,你必须在manifest文件中声明intent filter。然而,broadcast receiver的filter可以通过registerReceiver()动态的注册,然后通过unregisterReceiver()来解除注册。这样做可以让你的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.LUNCHER"/>
        </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的icon启动APP时,就会打开这个activity。
action: ACTION_MAIN , 表示这是APP主要的入口点,不期望任何intent data。
category:CATEGORY_LUNCHER ,表示这个activity的icon应该放到系统APP启动的地方。如果这个

使用一个Pending Intent

PendingIntent对象是对Intent对象的包装。PendingIntent的主要目的是允许外部应用使用Intent就像在自己的进程中执行一样。
Pending Intent的主要使用情况包括:

  • 声明一个Intent在用户执行Notification时执行(Android系统的NotificationManager执行这个Intent)。
  • 声明一个Intent当用户执行桌面部件时执行(主屏幕的小部件执行这个Intent)。
  • 声明一个Intent在将来某个时间点执行(Android 系统的AlarmManager执行这个Intent)。

因为设计每个Intent对象都是要被特定类型的APP组件处理(不论是Activity,Service还是BroadcastReceiver),所以处于相同的考虑,也必须创建一个PendingIntent。当使用PendingIntent时,你的app使用startActivity()方法将不会执行这个Intent。在你创建PendingIntent对象时,你必须声明想要启动的组件类型。创建PendingIntent对象,要调用响应的方法创建:

  • PendingIntent.getActivity(),获得用于启动Activity的Intent。
  • PendingIntent.getService(),获得用于启动Service的Intent。
  • PendingIntent.getBroadcast(),获得用于启动BroadcastReceiver的Intent。

除非你的APP用于接收来自其他APP的PendingIntent,否则的话你可能只需要使用上面的一个方法创建PendingIntent对象。
每一个方法都带有当前APP的Context,你想要包装的Intent,一个或多个指定intent怎样使用的标记位(比如说这个Intent能否多次使用)。
使用PendingIntent的更多信息在使用例子的相应文档中,比如:Notifications和APP widgetsAPI Guide。

Intent Resolution

当系统接收隐式intent来启动activity时,系统通过比较intent和intent-filter中的下面三方面来找到最佳的activity:

  • intent 的 action
  • intent 的 data(URI和data type)
  • intent 的 category
    下面的部分描述intent是怎样依据manifest文件中intent-filter来匹配合适的组件。
Action test

为了指定可接收的intent action,一个intent filter可以声明零个或多个

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

为了通过这个滤波器,Intent指定的action必须匹配滤波器列表中的一个action。
如果滤波器没有列出任何action,就没有action供Intent来匹配,所以任何intent都不会匹配成功。但是,如果一个Intent没有指定action,它将会通过滤波器的匹配(只要这个滤波器中至少有一个action)。
总结:滤波器中的action必须有,否则不能接收隐式Intent,而Intent的action可以没有,如果没有可以通过任何滤波器的匹配(只要这个滤波器可以接受隐式Intent,即至少有一个action)。

Category test

为了指定可以接收的Intent category,滤波器可以声明零个或者多个

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

一个intent要想通过category的测试,Intent中的每个category必须在滤波器中有一个匹配的(换句话说,就是Intent中有的滤波器中必须有)。滤波器中声明的category可能比intent中指定的多。所以,无论滤波器中声明多少category,没有指定category的intent总是可以通过category的测试。
Note: Android系统会自动为每个通过startActivity()或startActivityForResult()启动的隐式Intent添加CATEGORY_DEFAULT分类。所以,你想要你的activity接收隐式Intent的话,你需要在你的intent-filter中添加android.intent.category.DEFAULT分类。

Data test

为了指定可以接收的intent data,滤波器可以声明零个或者多个

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

每个

    URI:<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.

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

因为大多数可获得的数据都是由content provider分发的,滤波器指定数据类型而不是URI可能更为常见。
滤波器另一种常见的配置是scheme和data type。比如说,下面这个<data>元素告诉Android,这个组件执行的操作可以接收来自网络的video数据:

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

Intent matching

Intent 匹配滤波器不仅用于找到目标组件来启动,也用于找到设备上的一系列组件。举个例子,操作系统中存着app的启动器,就是通过intent-filter中的特殊action:ACTION_MAIN和特殊category CATEGORY_LAUNCHER来找到相应的activity。
你的app可以使用相似的方式进行intent匹配。PackageManager有一系列query…()方法来返回可以接收特定intent的所有组件,一系列resolve…()方法来找到响应intent最佳的组件。举个例子,queryIntentActivities()返回能够匹配intent的所有activity组件,queryIntentServices()返回能够匹配intent的所有service。没有方法激活组件,它们只是列出可以响应的组件。对于broadcast receivers来说,也有queryBroadcastReceivers()方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值