android之Intent基本使用

简介

  1. Intent本意为目的、意向、意图。在Android中,简言之,Intent是系统各组件(或应用程序)之间进行数据传递的数据负载者,Intent不仅可以用于应用程序之间的交互,也可以用于应用程序内部的activity、service和broadcast receiver之间的交互。
  2. 实际上,Intent是一种运行时绑定(run-time binding)机制,它能在程序运行过程中连接两个不同的组件。通过Intent,你的程序可以向Android表达某种请求或者意愿,Android会根据意愿的内容选择适当的组件来完成请求。Intent对象是一个被动的数据结构保存一个将要执行的操作的抽象描述,或在广播的情况下,通常是某事已经发生且正在宣告。Android的三个基本组件——Activity,Service和Broadcast Receiver都是通过Intent机制激活的,不同类型的组件有不同的传递Intent机制:

    1. 启动Activity

      1. 使用Context.startActivity()或Activity.startActivityForRestult()去启动一个Activity或使一个已存在的Activity去做新的事情。
      2. 使用Activity.setRestult()传入一intent来从activity中返回结果。

      具体详见1

    2. 启动Service

      1. 使用Context.startService()去初始化一个service或传递消息给正在运行的service。
      2. 使用Context.bindService()去建立调用组件和目标服务之间的连接(即绑定service)。

      具体详见2

    3. 启动broadcast receiver
      将intent对象传递给任何广播方法(如Context.sendBroadcast(),Context.sendOrderedBroadcast(),Context.sendStickyBroadcast()),都将传递到所有感兴趣的广播接收者。

    目前还没实现这部分,具体详见,,。
    只有广播不能绑定服务,能通启动。

在讲解Intent属性之前必须先了解一下系统如何通过Intent找到用户所希望的组件。这就要涉及到Intent Filter匹配。下面以在Activity中设置为例。

Intent Filter

Intent Filter 描述了一个组件愿意接收什么样的 Intent 对象,Android 将其抽象为 android.content.IntentFilter 类。在 Android 的 AndroidManifest.xml 配置文件中的<activity>标签中可以通过< intent-filter> 标签为一个 Activity 指定其 Intent Filter,以便告诉系统该 Activity 可以响应什么类型的 Intent对象。

当程序员使用 startActivity() 或startActivityForRestult()来启动另外一个 Activity 时,如果直接指定 了Intent 对象的 Component 属性(多种方式,下面有详细介绍),那么 Activity Manager 将试图启动其 Component 属性指定的 Activity。否则 Android 将通过 Intent 的其它属性从安装在系统中的所有 Activity 中查找与之最匹配的一个启动(或当优先级相同时通过列表让用户自己选择),如果没有找到合适的 Activity,应用程序会得到一个系统抛出的异常。这个匹配的过程如下3
 Intent Filter 的匹配过程
注:

  • URI数据即为属性data,此处意思表示必须匹配data或type,若同时出现则必须两者都匹配。
  • 三种检测(动作检测(Action),种类检测(Category),数据检测(包括data和type))

Intent的解析

Intent分类

在应用中,我们可以以两种形式来使用Intent:

  • 显式Intent,即指定组件形式
    指定了component属性的Intent(调用setComponent(ComponentName)或者setClass(Context, Class)来指定)或者直接用构造器Intent(Context,Class)。通过指定具体的组件类,通知应用启动对应的组件。
  • 隐式Intent,即通过Intent Filter过滤匹配
    没有指定comonent属性的Intent。这些Intent需要包含足够的信息,这样系统才能根据这些信息,在所有的可用组件中,确定满足此Intent的组件。

对于显式Intent,Android不需要去做解析,因为目标组件已经很明确,Android需要解析的是那些隐式Intent,通过解析将 Intent对象映射给可以处理此Intent的Activity、Service或Broadcast Receiver对象。

Intent解析机制

前面已经提到Intent解析机制主要是通过查找已注册在AndroidManifest.xml中的所有<intent-filter>及其中定义的Intent,通过PackageManager(注:PackageManager能够得到当前设备上所安装的
application package的信息,可以参见4)来查找能处理这个Intent的component。在这个解析过程中,Android是通过Intent的action、type或data、category这三个属性来进行判断的,判断方法如下:

  1. 如果Intent指明了action,则目标组件的IntentFilter的action列表中就必须包含有这个action,否则不能匹配;
  2. 如果Intent没有提供type,系统将从data中得到数据类型。和action一样,目标组件的数据类型列表中必须包含Intent的数据类型,否则不能匹配。
  3. 如果Intent中的数据不是content类型的URi,而且Intent也没有明确指定type,将根据Intent中数据的scheme(比如 http或者tel)进行匹配。同上,Intent 的scheme必须出现在目标组件的scheme列表中。
  4. 如果Intent指定了一个或多个category,这些类别必须全部出现在组件的类别列表中。

目前先了解如何匹配过程,后面应看如何实现的,即看源码可以参考5

Intent属性

Intent由以下各个组成部分:

  • component
    目的组件
  • action
    表示意图的行动
  • catecory
    表示动作的类别
  • data
    表示动作要操作的数据
  • type
    data的数据类型描述
  • extras
    扩展信息
  • flags
    设置组件的启动模式

下面具体介绍各个属性(以启动Activity为例)

组件Component

指定Intent对象的目标组件的类名称。指定component的话,将直接使用并启动它指定的组件,Intent的其它所有属性都是可选的。此为显式Intent,即直接启动指定的组件。

组件名字是可选的,如果设置了,intent对象传递到指定类的实例;如果没有设置,Android使用intent中的其它属性来定位合适的目标组件。组件的名字通过setComponent(),setClass()或setClassName()设置均可,通过getComponent()读取。其中,setComponent()接收一个ComponentName类实例,ComponentName构造器代码如下:

三种ComponentName构造器(源码)

/**
     * Create a new component identifier.
     * 
     * @param pkg The name of the package that the component exists in.  Can
     * not be null.
     * @param cls The name of the class inside of <var>pkg</var> that
     * implements the component.  Can not be null.
     */
    public ComponentName(String pkg, String cls) {
        if (pkg == null) throw new NullPointerException("package name is null");
        if (cls == null) throw new NullPointerException("class name is null");
        mPackage = pkg;
        mClass = cls;
    }
    /**
     * Create a new component identifier from a Context and class name.
     * 
     * @param pkg A Context for the package implementing the component,
     * from which the actual package name will be retrieved.
     * @param cls The name of the class inside of <var>pkg</var> that
     * implements the component.
     */
    public ComponentName(Context pkg, String cls) {
        if (cls == null) throw new NullPointerException("class name is null");
        mPackage = pkg.getPackageName();
        mClass = cls;
    }
    /**
     * Create a new component identifier from a Context and Class object.
     * 
     * @param pkg A Context for the package implementing the component, from
     * which the actual package name will be retrieved.
     * @param cls The Class object of the desired component, from which the
     * actual class name will be retrieved.
     */
    public ComponentName(Context pkg, Class<?> cls) {
        mPackage = pkg.getPackageName();
        mClass = cls.getName();
    }

其实,这三种构造器本质都一样。

显示Intent启动另一个Activity

代码如下:

/**
* 使用component属性设置Intent对象,启动一个Activity
 *
*/
Intent intent = new Intent();
//构造器有多种,也可以为new ComponentName(this,MainActivity2.class);
//同样也有启动其它包中activity的构造器
ComponentName componentName = new ComponentName(this,"com.sywyg.intent_test.MainActivity2");
intent.setComponent(componentName);
startActivity(intent);

需要注意的是,如果我们在Intent中指定了component属性,系统将不会再对action、data/type、category进行匹配。

动作action

表示意图的动作。例如日常生活中在描述一个意愿时经常带有某种动作,比如我想看爱情动作片,这里看就是一个动作。在Intent中,action就是描述意图的动作。当指明一个action时,目标组件就会依照这个动作的指示,表现对应的行为。Action是一个的字符串,也可以由用户自定义,在Intent中,定义了很多动作,如ACTION_VIEW等基本上涵盖了常用的动作。一个 Intent Filter 可以包含多个 Action。

在 AndroidManifest.xml 的 <activity> 标签中添加在< intent-filter>标签中指定一个 Action 列表用于说明 Activity 所能接受的“动作”,代码如下:

<activity
    android:name=".MainActivity2"
    android:label="@string/title_activity_main_activity2" >
    <intent-filter>
          <action android:name="com.sywyg.intent_test.MY_ACTION"/>
          <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

注:

  • “com.sywyg.intent_test.MY_ACTION”为自定义字符串,这里为了方便介绍,直接使用字符串。一般使用枚举类实现。
  • 每个Intent只能设置一个动作,而intent-filter中可以添加多个。
  • 若当前组件是Activity,则必须包含一个默认的类别,也有列外(下面有介绍)。

对应的activity中的代码如下:

 /**
  * 使用action属性设置Intent对象,启动匹配成功的Activity
  */
 Intent intent = new Intent();
 intent.setAction("com.sywyg.intent_test.MY_ACTION");
 //或直接使用构造器Intent("com.sywyg.intent_test.MY_ACTION")设置动作
 startActivity(intent);

所有 Action 列表中包含了”com.sywyg.intent_test.MY_ACTION”的 Activity 都将会匹配成功。如果就一个匹配的Activity则直接跳转到该Activity,如果有多个匹配的Activity,则按优先级排序,返回优先级最高的Activity。若优先级相同,则系统会通过对话框的方式让用户选择。

Android 预定义了一系列的 Action 分别表示特定的系统动作。这些 Action 通过常量的方式定义在 android.content. Intent中以“ACTION_”开头(枚举类实现的),可以在 Android 提供的文档中找到它们的详细说明。当然可以定义自己的动作字符串应在我们的应用程序中激活组件。自定义动作字符串通常应该包含应用程序包名前缀,如”com.sywyg.intent_test.MY_ACTION”。

动作很大程度上决定了剩下的intent如何构建,特别是数据(data和type)和类型(category)字段,就像一个方法名决定了参数和返回值。正是这个原因,应该尽可能明确指定动作,并紧密关联到其它Intent属性。换句话说,应该定义你的组件能够处理的Intent对象的整个协议,而不仅仅是单独地定义一个动作。

类别category

category用于表现动作的类别,相当于把动作分类(如增删改(三个动作)一篇论文,这三个动作都属于编辑类别),Intent和Intent Filter中都可以添加多个。

在 AndroidManifest.xml 的 Activity声明时可以在其 intent-filter 标签中为组件定义一个或多个 category 类别列表,当 Intent 中包含所有相同的Category 类别时匹配(除默认类别)才会成功。AndroidManifest.xml 代码如下:

<intent-filter>
   <action android:name="com.sywyg.intent_test.MY_ACTION"/>
   <category android:name="com.sywyg.intent_test.CATEGORY"/>
   <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>

对应Activity中的代码如下:

 /**
  * 使用category属性设置Intent对象,removeCategory()删除一个category,
getCategories()获取intent所有的category.
 */
 Intent intent = new Intent();
 intent.setAction("com.sywyg.intent_test.MY_ACTION");
 intent.addCategory("com.sywyg.intent_test.CATEGORY");
 startActivity(intent);

注:

  • 必须和动作一块设置,否则报错。
  • 若当前组件是Activity则,必须在intent-filter标签中再增加一个添加默认的类别:
    <category android:name="android.intent.category.DEFAULT"/>PS:多说一句看准IDE自动补全的是不是”android.intent.category.DEFAULT”,提示NO Activity。我在这纠结了半天。
  • 在主Activity中设置”android.intent.action.MAIN” 和”android.intent.category.LAUNCHER”,它们分别标记活动开始新的任务和带到启动列表界面。该Activity可以包含”android.intent.category.DEFAULT”,也可以不包含。
    -action 和category最好用枚举类实现。

数据data

data表示动作要操作的数据,更精确地指定当前活动能够响应什么类型的数据。data属性的声明中要指定访问数据的Uri和MIME类型,在<intent-filter>标签中的<data>中有如下主要属性:

  • android:scheme
    指定数据的协议部分,如http、https、tel…,还可以定义自己的前缀
  • android:host
    用于指定数据的主机名部分,如www.google.com,如果定义为“*”则表示任意主机名
  • android:port
    用于指定数据的端口部分,一般紧随在主机名之后。
  • android:path
    用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容。
  • android:mimeType
    用于指定可以处理的数据类型,允许使用通配符(模糊说明,如”audio/*”)的方式指定。

host和port一起构成URI的权限(authority),如果host没有指定,port也被忽略。这四个属性都是可选的,但它们之间并不都是完全独立的。要让authority有意义,scheme必须也要指定。要让path有意义,scheme和authority也都必须要指定。

通过这些属性来对应一个典型的Uri格式scheme://host:port/path,例如http://blog.csdn.net/wangyongge85

当比较intent对象和过滤器的URI时,仅仅比较过滤器中出现的URI属性。例如,如果一个过滤器仅指定了scheme,所有与此scheme的URIs都匹配过滤器;如果一个过滤器指定了scheme和authority,但没有指定path,所有匹配scheme和authority的URIs都通过检测,而不管它们的path;如果四个属性都指定了,要都匹配才能算是匹配。然而,过滤器中的path可以包含通配符来要求匹配path中的一部分。
不同的动作有不同的数据规格。例如(系统提供的),如果动作字段是ACTION_EDIT,数据字段将包含将显示用于编辑的文档的URI;如果动作是ACTION_CALL,数据字段将是一个tel:URI和将拨打的号码;如果动作是ACTION_VIEW,数据字段是一个http:URI,接收活动将被调用去下载和显示URI指向的数据。

在 AndroidManifest.xml 的 Activity 声明时可以在其intent-filter标签中通过<data>标签添加数据,当 Intent 中包含相同的data时匹配才会成功。代码如下:

 <intent-filter
  android:priority="100">
    <data android:scheme="http" android:host="blog.csdn.net"
    android:path = "wangyongge85"/>
 </intent-filter>

注:

  1. android:priority属性设置优先级(-1000到1000,必须有个负值才生效)
  2. <data>标签一般不需要指定过多的内容,如上只需指定android:scheme属性,就可以响应所有的http协议的Intent了。
  3. 协议一般还有geo表示显示地理位置,tel表示拨打电话
  4. 调用系统自带的应用需设置相应的action和data,见Android系统中标准Intent的使用

对应Activity中的代码如下:

/**
 * 使用data属性设置Intent对象
 */
Intent intent = new Intent();               intent.setAction("com.sywyg.intent_test.MY_ACTION");                intent.addCategory("com.sywyg.intent_test.CATEGORY");  

Uri data = Uri.parse("http://www.sywyg.com");

intent.setData(data);       
//拨打电话:intent.setData(Uri.parse("tel:10086"));
startActivity(intent);

注:

  • 实际上一个data是用一个Uri对象表示
  • 通常情况下使用action + data属性的组合来描述一个意图:做什么。

数据类型type

指定data属性的数据类型或MIME类型(如text/html,text/xml,image/jpg等),但是通常来说,当Intent不指定data属性是type属性才有效,否则系统data属性值分析数据类型,因此无须指定type。
数据类型(type)是将作用于其上的数据的URI和数据的MIME类型。
在AndroidManifest.xml中声明,在data标签中设置
<data android:mimeType="text/html" />,type属性指定数据的MIME类型。Intent对象和过滤器都可以用”*”通配符匹配子类型字段,例如”text/*”,”audio/*”表示任何子类型。

数据检测既要检测URI,也要检测数据类型。规则如下:

  • 一个Intent对象既不包含URI,也不包含数据类型:仅当过滤器也不指定任何URIs和数据类型时,才不能通过检测;否则都能通过。
  • 一个Intent对象包含URI,但不包含数据类型:仅当过滤器也不指定数据类型,同时它们的URI匹配,才能通过检测。例如,mailto:和tel:类型都不指定实际数据。
  • 一个Intent对象包含数据类型,但不包含URI:仅当过滤也只包含数据类型且与Intent相同,才通过检测。
  • 一个Intent对象既包含URI,也包含数据类型(或数据类型能够从URI推断出):数据类型部分,只有与过滤器中之一匹配才算通过;URI部分,它的URI要出现在过滤器中,或者它有content:或file: URI,又或者过滤器没有指定URI。换句话说,如果它的过滤器仅列出了数据类型,组件假定支持content:和file: 。

当匹配一个intent到一个能够处理数据的组件,通常知道数据的类型(它的MIME类型)和它的URI很重要。例如,一个组件能够显示图像数据,不应该被调用去播放一个音频文件。

在许多情况下,数据类型能够从URi中推测,特别是content:URIs,它表示位于设备上的数据且被内容提供者(content provider)控制。但是类型也能够显示地设置,setData()方法指定数据的URi,setType()指定MIME类型(例如:”audio/mp3”),setDataAndType()指定数据的URI和MIME类型。通过getData()读取URI,getType()读取类型。

 /**
  * 使用type属性设置Intent对象
  */
  Intent intent = new Intent();
  //通常要设置动作和种类,配合data或type
  intent.setType("audio/mp3");
  startActivity(intent);

通过setData()方法会把type设置为null,相反通过setType()能把data设置为null,如果同时设置两个使用setDataAndType()方法。

扩展信息extras

附加信息,一般用来携带一些数据信息。通过额外的键值对把数据保存在Intent对象中。

Intent对象有一系列的put…()方法用于插入各种附加数据和一系列的get…()用于读取数据。这些方法与Bundle对象的方法类似,实际上,附加信息可以作为一个Bundle使用putBundleExtras()和getBundleExtras()安装和读取。
已在Activity通信中详解,见6

标志位flags

期望意图的运行模式。Intent类定义了各种各样的标志,许多指示Android系统如何去启动一个活动(例如,活动应该属于那个任务)和启动之后如何对待它(例如,它是否属于最近的活动列表)。
在这里不多讲,后续将结合Activity的启动模式一块讲解,或请参见郭哥出品的文章。
目前只知道对于Activity来讲这里是设置Activity的启动模式的,包括四种详见(还没发表)。也可在Intent对象中通过setFlags()方法设置。

吴秦http://www.cnblogs.com/skynet/archive/2010/07/20/1781644.html
http://www.oschina.net/question/565065_67909
威哥视频
郭霖第一行

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值