《疯狂Android讲义》学习笔记 -- 使用Intent和IntentFilter进行通信

1、前言:

          当一个Activity需要启动另一个Activity时,程序并没有直接告诉系统要启动哪个Activity,而是通过Intent来表达自己的意图:需要启动哪个Activity。Intent的中文翻译就是“意图”的意思。
         在这里有读者可能会产生一个疑问,假如甲Activity需要启动另一个Activity,为何不直接使用一个形如startActivity(Class activityClass)的方法呢?这样多么简单、明了啊。但实际上,这种方式虽然简洁,却明显背离了Android的理念,Android使用Intent来封装程序的“调用意图”,不管程序想启动一个Activity也好,想启动一个Service组件也好,想启动一个BroadcastReceiver也好,Android使用统一的Intent对象来封装这种“启动意图”,很明显使用Intent提供了一致的编程模型。
         除此之外,使用Intent还有一个好处:在某些时候,应用程序只是想启动具有某种特征的组件,并不想和某个具体的组件耦合,如果使用形如startActivity(Class activityClass)的方法来启动特定组件,势必造成一种硬编码耦合,这样也不利于高层次的解耦。
         总之,Intent封装Android应用程序需要启动某个组件的“意图”。不仅如此,Intent还是应用程序组件之间通信的重要媒介,如两个Activity可以把需要交换的数据封装成Bundle对象,然后使用Intent来携带Bundle对象,这样就实现了两个Activity之间的数据交换。

2、Intent对象简述

         Android的应用程序包含三种重要组件:Activity,Service,BroadcastReceiver,应用程序采用了一致的方式来启动它们----都是依靠Intent来启动的,Intent就封装了程序想要启动程序的意图。不仅如此,Intent还可以用于与被启动组件交换信息。
                   表1.1显示了使用Intent启动不同组件的方法。
         表1.1     使用Intent启动不同组件的方法
组件类型 启动方法
Activity startActivity(Intent intent)
startActivityForResult(Intent intent,int requestCode)
Service startService(Intent intent)
bindService(Intent intent,ServiceConnection conn, int flags)
BroadcastReceiver sendBroadcast(Intent intent)
sendBroadcast(Intent intent, String receiverPermission)
sendOrderedBroadcast(Intent intent, String receiverPermission)
...
         Intent对象大致包含Component、Action、Category、Data、Type、Extra和Flag这7中属性,其中Component用于明确指定需要启动的目标组件,而Extra则用于“携带”需要交换的数据。

3、Intent的属性及intent-filter配置

3.1、Component属性

         Intent的Component属性需要接受一个ComponentName对象,ComponentName对象包含如下几个构造器。
  • ComponentName(String pkg, String cls):创建pkg所在包下的cls类所对应的组件。
  • ComponentName(Context pkg, String cls):创建pkg所在包下的cls类所对应的组件。
  • ComponentName(Context pkg, Class<?> cls):创建pkg所在包下的cls类所对应的组件。
         上面几个构造器的本质是相同的,这说明创建一个ComponentName需要指定包名和类名----这就可以唯一地确定一个组件类,这样应用程序即可根据给定的组件类启动特定的组件。
         除此之外,Intent还包含了如下三个方法。
  • setClass(Context packageContext, Class<?> cls):设置该Intent将要启动的组件对应的类。
  • setClassName(Context packageContext, String className):设置该Intent将要启动的组件对应的类名。
  • setClassName(String packageName, String className):设置该Intent将要启动的组件对应的类名。
         指定Component属性的Intent已经明确了它将要启动哪个组件,因此这种Intent也被称为显式Intent,没有指定Component属性的Intent被称为隐式Intent----隐式Intent没有明确指定要启动哪个组件,应用将会根据Intent指定的规则去启动符合条件的组件,但具体是哪个组件则不确定。
         下面的示例程序示范了如何通过显示Intent(指定了Component属性)来启动另一个Activity。该程序的Java代码如下:
   
   
ComponentName componentName = new ComponentName(MainActivity.this,SecondActivity.class);
Intent intent = new Intent();
// 为intent设置Component属性
intent.setComponent(componentName);
startActivity(intent);
         上面程序中代码用于创建ComponentName对象,并将该对象设置成Intent对象的Component属性,这样应用程序即可根据该Intent的“意图”去启动指定组件。
         实际上,上面代码完全可以简化为如下形式:
   
   
// 根据指定组件类来创建Intent
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
         从上面的代码可以看出,当需要为Intent设置Component属性时,实际上Intent已经提供了一个简化的构造器,这样方便程序直接指定启动其他组件。Intent构造器源码如下:
   
   
public Intent(Context packageContext, Class<?> cls) {
mComponent = new ComponentName(packageContext, cls);
}
         当程序通过Intent的Component属性(明确指定了启动哪个组件)启动特定组件时,被启动组件几乎不需要使用<intent-filter.../>元素进行配置。

3.2、Action、Category属性与intent-filter配置

        Intent的Action、Category属性的值都是一个普通的字符串,其中Action代表该Intent所要完成的一个抽象“动作”,而Category则用于为Action增加额外的附加类别信息,通常Action属性会与Category属性结合使用。
         Action要完成的只是一个抽象动作,这个动作具体由哪个组件(或许是Activity,或许是BroadcastReceiver)来完成,Action这个字符串本身并不管。比如Android提供的标准Action:Intent.ACTION_VIEW,它只表示一个抽象的查看操作,但具体查看什么、启动哪个Activity来查看,Intent.ACTION_VIEW并不知道----这取决于Activity的<intent-filter.../>配置,只要某个Activity的 <intent-filter.../>配置中包含了该ACTION_VIEW,该Activity就有可能被启动。
         下面通过一个简单的实例来示范Action属性的作用,代码如下:
   
   
Intent intent = new Intent();
intent.setAction("com.yzx.test.secondactivity");
startActivity(intent);
         上面程序中的代码指定了根据Intent来启动Activity----但该Intent并未以“硬编码”的方式指定要启动哪个Activity,相信读者从上面程序中无法看出该程序将要启动哪个Activity。那么到底程序会启动哪个Activity呢?这取决于Activity配置中<intent-filter.../>元素的配置。
         <intent-filter.../>元素是Androidmanifest.xml文件中<activity.../>元素的子元素, <activity.../>元素用于为应用程序配置Activity, <activity.../>的 <intent-filter.../>子元素则用于配置该Activity所能“响应”的Intent。
          <intent-filter.../>元素里通常可包含如下子元素
  • 0~N个<action.../>子元素
  • 0~N个<category.../>子元素
  • 0~1个<data.../>子元素
          <action.../>、 <category.../>子元素的配置非常简单,它们都可指定android:name属性,该属性的值就是一个普通字符串。
         当<activity.../>元素的<intent-filter.../>子元素里面包含多个<action.../>子元素(相当于指定了多个字符串)时,就表明该Activity能响应Action属性值为其中任意一个字符串的Intent。
         由于上面的程序指定启动Action属性为com.yzx.test.secondactivity的Activity,也就要求被启动的Activity对应的配置元素的<intent-filter.../>元素里至少包括一个如下的<action.../>子元素:
   
   
<action android:name="com.yzx.test.secondactivity" />
         需要指出的是,一个Intent对象最多只能包括一个Action属性,程序可调用Intent的setAction(String str)方法来设置Action属性值;但一个Intent对象可以包含多个Category属性,程序可调用Intent的addCategory(String str)方法来为Intent添加Category属性。当程序创建Intent时,该Intent默认启动Category属性值为Intent.CATEGORY_DEFAULT常量(常量值为android.intent.category.DEFAULT)的组件。
         因此,虽然上面程序中没有指定目标Intent的Category属性,但该Intent已有一个值为android.intent.category.DEFAULT的Category属性值,因此被启动Activity对应的配置元素的<intent-filter.../>元素里至少还包括一个如下的<category.../>子元素:
   
   
<category android:name="android.intent.category.DEFAULT" />

3.3、Data、Type属性与intent-filter配置

         Data属性通常用于向Action属性提供操作的数据。Data属性接受一个Uri对象,一个Uri对象通常通过如下形式的字符串来表示:
  
  
content://com.android.contacts/contacts/1
tel:123
         Uri字符串总满足如下格式:
   
   
scheme://host:port/path
         例如上面给出的content://com.android.contacts/contacts/1,其中content是scheme部分,com.android.contacts是host部分,port部分被省略了,/contacts/1是path部分。
         Type属性用于指定该Data属性所指定Uri对应的MIME类型,这种MIME类型可以是任何自定义的MIME类型,只要符合abc/xyz格式的字符串即可。
         Data属性与Type属性的关系比较微妙,这两个属性会相互覆盖,例如:
  • 如果为Intent先设置Data属性,后设置Type属性,那么Type属性将会覆盖Data属性。
  • 如果为Intent先设置Type属性,后设置Data属性,那么Data属性将会覆盖Type属性。
  • 如果希望Intent既有Data属性,也有Type属性,则应该调用Intent的setDataAndType()方法。
         在AndroidManifest.xml文件中为组件声明Data、Type属性都通过<data.../>元素,<data.../>元素的格式如下:
   
   
<data android:mimeType=""
android:scheme=""
android:host=""
android:port=""
android:path=""
android:pathPrefix=""
android:pathPattern=""
/>
         上面的<data.../>元素支持如下属性。
  • mimeType:用于声明该组件所能匹配的Intent的Type属性。
  • scheme:用于声明该组件所能匹配的Intent的Data属性的scheme部分。
  • host:用于声明该组件所能匹配的Intent的Data属性的host部分。
  • port:用于声明该组件所能匹配的Intent的Data属性的port部分。
  • path:用于声明该组件所能匹配的Intent的Data属性的path部分。
  • pathPrefix:用于声明该组件所能匹配的Intent的Data属性的path前缀。
  • pathPattern:用于声明该组件所能匹配的Intent的Data属性的path字符串模板。
         Intent的Type属性也用于指定该Intent的要求,对应组件中<intent-filter.../>元素的<data.../>子元素的mimeType属性必须与此相同,才能启动该组件。
         Intent的Data属性则略有差异,程序员为Intent指定Data属性时,Data属性的Uri对象实际上可分为scheme、host、port和path部分,此时并不要求被启动组件的<intent-filter.../>中<data.../>子元素的android:scheme、android:host、android:port、android:path完全满足。
         Data属性的“匹配”过程则有些差别,它会先检查<intent-filter.../>里的<data.../>子元素,然后:
  • 如果目标组件的<data.../>子元素只指定了android:scheme属性,那么只要Intent的Data属性的scheme部分与android:scheme属性值相同,即可启动该组件。
  • 如果目标组件的<data../>子元素只指定了android:scheme、android:host属性,那么只要Intent的Data属性的scheme、host部分与android:scheme、android:host属性值相同,即可启动该组件。
  • 如果目标组件的<data../>子元素只指定了android:scheme、android:host、android:port属性,那么只要Intent的Data属性的scheme、host、port部分与android:scheme、android:host、android:port属性值相同,即可启动该组件。(注意:如果<data.../>子元素只有android:port属性,没有指定android:host属性,那么android:port属性将不会起作用)。
  • 如果目标组件的<data../>子元素只指定了android:scheme、android:host、android:path属性,那么只要Intent的Data属性的scheme、host、path部分与android:scheme、android:host、android:path属性值相同,即可启动该组件。(注意:如果<data.../>子元素只有android:path属性,没有指定android:host属性,那么android:path属性将不会起作用)。
  • 如果目标组件的<data../>子元素只指定了android:scheme、android:host、android:port、android:path属性,那么只要Intent的Data属性的scheme、host、port、path部分与android:scheme、android:host、android:port、android:path属性值相同,即可启动该组件。

3.4、Extra属性

         Intent的Extra属性通常用于在多个Activity之间进行数据交换,Intent的Extra属性值应该是一个Bundle对象,Bundle对象就像一个Map对象,它可以存入多个key-value对,这样就可以通过Intent在不同Activity之间进行数据交换了。

3.5、Flag属性

         Intent的Flag属性用于为该Intent添加一些额外的控制旗标,Intent可调用addFlags()方法来添加控制旗标。
         Intent包含了如下常用的Flag旗标。
  • FLAG_ACTIVITY_BROUGHT_TO_FRONT:如果通过该Flag启动的Activity已经存在,下次再次启动时,将只是把该Activity带到前台。例如,现在Activity栈中有Activity A,此时以该旗标启动Activity B(即Activity B是以FALG_ACTIVITY_BROUGHT_TO_FRONT旗标启动的),然后在Activity B中启动Activity C、D,如果此时在Activity D中再启动Activity B,将直接把Activity栈中的Activity B带到前台。此时Activity栈中情形是Activity A、C、D、B。
  • FLAG_ACTIVITY_CLEAR_TOP:该Flag相当于加载模式中的singleTask,通过这种Flag启动的Activity将会把要启动的Activity之上的Activity全部弹出Activity栈。例如,Activity栈中包含A、B、C、D四个Activity,如果采用该Flag从Activity D跳转到Activity B,那么此时Activity栈中只包含A、B两个Activity。
  • FLAG_ACTIVITY_NEW_TASK:默认的启动旗标,该旗标控制重新创建一个新的Activity。
  • FALG_ACTIVITY_NO_ANIMATION:该旗标控制启动Activity时不适用过渡动画。
  • FALG_ACTIVITY_NO_HISTORY:该旗标控制被启动的Activity将不会保留在Activity栈中。例如,Activity栈中原来有A、B、C三个Activity此时在Activity C中以该Flag启动Activity D,Activity D再启动Activity E,此时Activity栈中只有A、B、C、E四个Activity,Activity D不会保留在Activity。
  • FLAG_ACTIVITY_REORDER_TO_FRONT:该Flag控制如果当前已有Activity,则直接将该Activity带到前台。例如,现在Activity栈中有A、B、C、D四个Activity,如果使用FLAG_ACTIVITY_REORDER_TO_FRONT旗标来启动Activity B,那么启动后的Activity栈中情形为A、C、D、B。
  • FLAG_ACTIVITY_SINGLE_TOP:该Flag相当于加载模式中的singleTop模式。例如,原来Activity栈中有A、B、C、D四个Activity,在Activity D中再次启动Activity D,Activity栈中依然还是A、B、C、D四个Activity。
         Android为Intent提供了大量的Flag,每个Flag都有其特定的功能,具体请参考关于Intent的API文档。

4、总结

         本章主要介绍了Android系统中Intent的功能和用法,当Android应用需要启动某个组件时,总需要借助于Intent来实现。不管是启动Activity,还是启动Service、BroadcastReceiver组件,Android系统都是由Intent来实现的。简单地说,Android使用Intent封装了应用程序的“启动意图”,但这种“意图”并未直接与任何程序组件耦合,通过这种方式即可很好地提高系统的可扩展性和可维护性。学习本章需要重点掌握Intent的Component、Action、Category、Data、Type各属性的功能和用法,并掌握如何在AndroidManifest.xml文件中配置<intent-filter.../>元素。

5、参考资料

  1. 《疯狂Android讲义》
  2. android官方文档:https://developer.android.com/guide/components/intents-filters.html








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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值