`
hyongbai
  • 浏览: 15277 次
文章分类
社区版块
存档分类
最新评论

组件间通信 Intent IntentFilter

 
阅读更多

Understanding Intent and IntentFilter--理解Intent和IntentFilter

Intent(意图)在Android中是一个十分重要的组件,它是连接不同应用的桥梁和纽带,也是让组件级复用(Activity和 Service)成为可能的一个重要原因。Intent的使用分为二个方面一个是发出Intent,另一个则是接收Intent用官方的说法就是Intent Resolving。本主将对Intent和IntentFilter进行一些介绍。
Intent和IntentFilter是Android和一种消息通信机制,可以让系统的组件之间进行通信。信息的载体就是Intent,它可以是一个要完成的动作请求,也可以一般性的消息广播,它可以由任意一个组件发出。消息,也就是Intent,最终也是要被组件来进行处理和消化。消息由组件发出,通常在消息的里面也会有会标记有目标组件的相关信息,另外目标组件也需要告诉系统,哪些消息是它所感兴趣的,它需要设置一些过滤器,以过滤掉无关的消息。
其实这里就好比学校里的广播,广播有时会播放通知,但有时也会播放要执行的动作,比如打扫卫生。消息中通常都会说明消息的目标对象,可能是计算机学院,可能是老师,也可能是英语系的人才需要关注。而每个人,或是学院组织,也只关心与他们有关的消息,当然这里就要他们自己去判断哪些是与他们有关的消息了。在Android当中消息就是Intent,过滤器就是IntentFilter。发出消息的组件可以在消息中设置目标组件的相关信息,目标组件也可以设置过滤器,然后系统会进行过滤,只把组件所感兴趣的消息,传递给组件。这里假设读者已经了解Intent和IntentFilter的基本使用方法,且并不会进行全面的介绍,如果不了解,可以先读读官方文档,这里重点讲讲IntentFilter在使用时的一些注意事项。
Intent消息机制通常有二种,一个是显式Intent(Explicit Intent),另一个是隐式Intent(Implicit Intent)。
  • 显式Intent--需要在Intent中明确指定目标组件,也就是在Intent中明确写明目标组件的名称(Component name),需要指定完整的包名和类名。因为对于本程序以外的其他应用程序,你很难知道它的组件名字,所以显式的Intent通常用于应用程序内部通信,更确切的说,显示Intent是用于应用程序内部启动组件,通常又是Activity或Service。还没有见用显式Intent来给BroadcastReceiver发送广播的。
  • 隐式Intent--也就是不在Intent中指定目标组件,在Intent中不能含有目标的名字。系统是根据其他的信息,比如Data,Type和Category去寻找目标组件。
隐式Intent通常用于与应用程序外部的组件进行通信。应用程序级别的组件复用也主要是靠隐式Intent来完成的。而IntentFilter也是只有隐式Intent才用的着,显式Intent都是直接把Intent传递给目标组件,根本不会理会组件的IntentFilter。

显式Intent(Explicit Intent)

显示Intent使用起来比较简单,只需要在Intent中指定目标组件的名字即可,也就是通过Intent的方法设置Component属性。如前所述,显式Intent通常用于应用程序内部启动Activity或Service。事实上,并不完全局限在应用程序内部,对于外部应用的Activity和Service,也可以用显式Intent来启动,但你必须知道它们的名字。
设置组件的名字的方法有:
  1. publicIntentsetComponent(ComponentNamecomponent);
  2. publicIntentsetClass(ContextpackageContext,Class<?>cls);
  3. publicIntentsetClassName(ContextpackageContext,StringclassName);
  4. publicIntentsetClassName(StringpackageName,StringclassName);
事实上虽然设置的方法有这么多,但Intent内部标识目标组件的属性只有一个Component,所以这么设置方法的目的也只是设置目标组件的Component,事实上有这么多的方法原因在于ComponentName的构造是多重载了的。在解析Intent时,系统也是取得这个Component属性,然后去启动它。
ComponentName Intent.getComponent();
对于应用程序内部启动Activity通常是这样子设置Intent:
  1. Intenti=newIntent();
  2. //Selectoneofthem
  3. i.setComponent(newComponentName(getApplication(),ViewStubDemoActivity.class));
  4. i.setComponent(newComponentName(getApplication(),ViewStubDemoActivity.class.getName()));
  5. i.setComponent(newComponentName(getApplication().getPackageName(),ViewStubDemoActivity.class.getName()));
  6. i.setClass(getApplication(),ViewStubDemoActivity.class);
  7. i.setClassName(getApplication(),ViewStubDemoActivity.class.getName());
  8. i.setClassName(getApplication().getPackageName(),ViewStubDemoActivity.class.getName());
  9. startActivity(i);
因为应用程序内部的组件类,都是可以访问到的,所以要尽可能少写字串常量,以减少拼写错误,如果一定要使用包名和类名,也要注意,类名必须是全称,也就是从包名开始,如“com.hilton.networks.WifiManagerActivity"。
但是对于外部应用程序的Activity,通常只能通过以下方法:
  1. Intenti=newIntent();
  2. //selectoneofthem
  3. i.setComponent(newComponentName("com.hilton.networks","com.hilton.networks.WifiManagerActivity"));
  4. i.setClassName("com.hilton.networks","com.hilton.networks.WifiManagerActivity");
  5. startActivity(i);
首先,带有Context为参数的是不能够用的,因为通常你无法拿到其他应用程序的Context,你只能拿到你所在应用程序的Context,所以用你所在的应用程序的Context去启动外部的Activity肯定会报错的。其次,不参再像上面那样通过Class.getName()去指定类名,你为你无法导入外部的类,会有编译错误的。另外,千万要注意不要拼错,否则会有RuntimeException抛出的。
对于Service组件,也是一样,Intent的写法与Activity组件一致,但是对于BroadcastReceiver组件通常都用显式Intent。

隐式Intent的消息过滤器--IntentFilter

IntentFilter是用来解析隐式Intent(Implicit Intent)的,也就是说告诉系统你的组件(Activity, Service, BroadcastReceiver)能够处理哪些隐式的Intent。在使用的时候我们通常是这样子的:
  1. <manifest...>
  2. <receiver...>
  3. <intent-filter>
  4. <actionandroid:name="android.appwidget.action.APPWIDGET_UPDATE"/>
  5. <actionandroid:name="android.appwidget.action.APPWIDGET_ENABLED"/>
  6. <actionandroid:name="android.appwidget.action.APPWIDGET_DISABLED"/>
  7. <actionandroid:name="android.appwidget.action.APPWIDGET_DELETED"/>
  8. </intent-filter>
  9. <intent-filter>
  10. <actionandroid:name="android.intent.action.MEDIA_MOUNTED"/>
  11. <actionandroid:name="android.intent.action.MEDIA_UNMOUNTED"/>
  12. <actionandroid:name="android.intent.action.MEDIA_SHARED"/>
  13. <actionandroid:name="android.intent.action.MEDIA_REMOVED"/>
  14. <actionandroid:name="android.intent.action.MEDIA_EJECT"/>
  15. <dataandroid:scheme="file"/>
  16. </intent-filter>
  17. <intent-filter>
  18. <actionandroid:name="android.intent.action.PACKAGE_ADDED"/>
  19. <actionandroid:name="android.intent.action.PACKAGE_REMOVED"/>
  20. <actionandroid:name="android.intent.action.PACKAGE_DATA_CLEARED"/>
  21. <dataandroid:scheme="package"/>
  22. </intent-filter>
  23. </receiver>
  24. </manifest>
在Manifest中使用IntentFilter时要注意以下三点:
1. 千万注意拼写错误
这里有一个需要十分小心和注意的地方那就是对于IntentFilter里面的Action和Data字串常量不要写错,因为这个在编译时是不会被检查,在运行时又不会抛出异常,如果你拼写错了,比如大小写拼错了,在编译时和运行时都不会有错误,但是你的程序却不能正常工作,你的程序无法收到相应的Intent。曾有一个同事在IntentFilter中写了一些Action,但把其中一个的大小写拼错了,结果花了他一个下午的时间来调试,最后还是另外一个同事到他那聊天才发现了是大小写的拼写错误。
这里也可以发现Android在Manifest文件中的IntentFilter这块的封装性很差。如果,仅仅是如果,这些Action常量也可以通过引用的方式来写,就可以在编译时做些检查和匹配,可以大大的减少出错的机率,同时也加强了封装和信息隐藏。比如,对于上面的可以写成这样:
  1. <manifest...>
  2. <receiver...>
  3. <intent-filter>
  4. <actionandroid:name="@android:action/AppWidgetManager.APPWIDGET_UPDATE"/>
  5. <actionandroid:name="@android:action/AppWidgetManager.APPWIDGET_ENABLED"/>
  6. <actionandroid:name="@android:action/AppWidgetManager.APPWIDGET_DISABLED"/>
  7. <actionandroid:name="@android:action/AppWidgetManager.APPWIDGET_DELETED"/>
  8. </intent-filter>
  9. </receiver>
  10. </manifest>
虽然这种拼写错误很低级,但是因为它低级所以当程序不能正常工作时没有人会想到是因为拼写错误,所以这种拼写错误通常会耗费不少的调试时间。另外一种避免此种错误的方法就是在代码中通过Context.registerReceiver(BroadcastReceiver,IntentFilter)来注册BroadcastReceiver,就可以直接写入常量,而非具体字串。但这只能是接收Broadcast的时候,对于那些想作为公开接口的组件,还是需要在Manifest里面声明,比如Email,它要能处理Intent.ACTION_SEND_TO,就需要在Manifest中声明。2. 要注意Data字段除了上面讨论的之外,对于IntentFilter还有另外的一点需要注意,就是对于某些Action是需要加上Data字段信息,否则有可能接收不到。比如:
  1. <manifest...>
  2. <receiver...>
  3. <intent-filter>
  4. <actionandroid:name="android.intent.action.MEDIA_MOUNTED"/>
  5. <actionandroid:name="android.intent.action.MEDIA_UNMOUNTED"/>
  6. <actionandroid:name="android.intent.action.MEDIA_SHARED"/>
  7. <actionandroid:name="android.intent.action.MEDIA_REMOVED"/>
  8. <actionandroid:name="android.intent.action.MEDIA_EJECT"/>
  9. <dataandroid:scheme="file"/>
  10. </intent-filter>
  11. <intent-filter>
  12. <actionandroid:name="android.intent.action.PACKAGE_ADDED"/>
  13. <actionandroid:name="android.intent.action.PACKAGE_REMOVED"/>
  14. <actionandroid:name="android.intent.action.PACKAGE_DATA_CLEARED"/>
  15. <dataandroid:scheme="package"/>
  16. </intent-filter>
  17. </receiver>
  18. </manifest>
对于手机外部存储卡的状态变化的Broadcast,在注册监听器的时候就需要加上DataScheme,否则就会接收不到。这个也花费了我几个小时的调试时间,改在代码中用Context.registerReceiver(BroadcastReceiver,IntentFilter)注册也不行,最后参考了Music中的做法,加上了DataScheme才能在onReceive()中接收到Intent。同样对于后面的Package相关的Broadcast,也是要加上DataScheme否则也是接收不到Broadcast。可悲的是对于像这样的系统公共的Broadcast
Intent,在Intent的文档中并没有说明如何使用,如果没有参考事例,相信需要一定的时间才能够找出为什么接收不到Intent。
除了DataScheme还有一个是MimeType,这个对于系统公共接口是必须加上的,比如Email要处理Intent.ACTION_SENTTO,就需要这样声明:
  1. <manifest...>
  2. <activityandroid:name="EmailComposer">
  3. <intent-filter>
  4. <actionandroid:name="android.intent.action.SEND"/>
  5. <dataandroid:mimeType="image/*"/>
  6. </intent-filter>
  7. </activity>
  8. </manifest>
3. 同时也要注意Category字段
如果没有对IntentFilter写正确的Category字段,也是收不到Intent。比如:
  1. <manifest...>
  2. <receiver...>
  3. <intent-filter>
  4. <actionandroid:name="com.hilton.controlpanel.action.BUTTON_ACTION"/>
  5. <categoryandroid:name="com.hilton.controlpanel.category.SELF_MAINTAIN"/>
  6. </intent-filter>
  7. </receiver>
  8. </manifest>
如果把Category去掉,死活也接收不到Intent,当然这要取决于Intent是如何发出的,如果Intent发出时没有加Category,那就没有必须在IntentFilter加上Category。
总之,对于Intent,要保证发出和接收完全一致,否则系统就无法找到相应的匹配,程序也就无法接收Intent。
有关于 DEFAULT category,也要注意,如果是针对Activity的Implicit Intent隐式Intent,如果在没有其他Category的情况下,一定要加上DEFAULT Category。因为系统会在Context.startActivity(Intent)和Context.startActivityForResult(
Intent)时给Intent加上DEFAULT category。而对于Context.sendBroadcast(Intent),Context.sendOrderedBroadcast(Intent),Contxt.sendStickyBroadcast(Intent)和Context.bindService(Intent)Context.startService(Intent)就不会加DEFAULT Category。
另外要注意,尽量把Action进行合并写进一个IntentFilter中。因为对于每个IntentFilter标签都会创建一个IntentFilter对象,所以如果写几个就会有几个对象在那,不但耗费资源而且在匹配的时候也会耗费更多的时间,因为在查询匹配的时候是要一个IntentFilter对象接着一个IntentFilter对象进行检查的。直到找到最佳匹配或是到所有的IntentFilter都检查完为止。

IntentFilter的匹配规则

1. 通过Action字段来匹配这个是Intent中比较基本的一个字段,也比较简单,就是一个字串,如果相等就匹配成功,否则证明还没找到目标。但要注意,如果IntentFilter没有指定Action,那么它不会匹配到任何的隐式Intent,它只能被显式的Intent匹配上。反过来,如果Intent自己没有指定Action,那么它能匹配上含有任何Action的IntentFilter,但不能匹配上没有指定Action的IntentFilter。对于Action,平时要注意拼写错误,因为在AndroidManifest文件中声明Action都是字串,并且在编译时不会做检查,运行时,如果Action拼错了导致匹配不上,要么是程序不能正常工作,要么会有异常抛出。

2. 通过Category字段来匹配对于Activity来讲,如果想处理隐式Intent,并且除了Intent.ACTION_MAIN以外,必须指定Category为DEFAULT,否则不会被匹配到。因为Context.startActivity()和Context.startActivityForResult()会自动加上DEFAULT Category。其他情况,Service和BroadcastReceiver则不会,对于Service和BroadcastReceiver,如果Intent中没有指定Category,那么在其IntentFilter中也不必指定。

3. 通过Data字段来匹配这个相对来讲比较复杂,通常Data包含uri, scheme(content, file, http)和type(mimeType)对于Intent来讲有二个方法:

  1. Intent.setData(Uri);//一个Uri,Scheme包含在其中
  2. Intent.setType(String);//指定MimeType,比如'image/jpeg','audio/mpeg'等
  3. Intent.setDataAndType(Uri,String);//上面二个方法的简便调用方式,一起搞进去
对于IntentFilter来讲,需要设置的是Scheme和Type,Scheme是对Uri的限制,目标需要限制Scheme是因为Scheme能告诉目录能从哪里拿到Uri所指向的文件,Type是MimeType对类型的限制。
  1. <intent-filter>
  2. <actionandroid:name="android.intent.action.SEND"/>
  3. <categoryandroid:name="android.intent.category.DEFAULT"/>
  4. <dataandroid:scheme="content"android:mimeType="image/*"/>
  5. </intent-filter>
Data匹配时的规则一共有四条:
a.如果Intent没有指定Data相关的字段,只能匹配上没有指定Data的IntentFilter。也就是说如果一个Intent没有指定任何的Data(Uri和Type),它只能匹配到没有指定任何Data(Scheme和Type)的IntentFilter。
b.如果一个Intent只指定了Uri但是没有Type(并且Type也不能够从Uri中分析出)只能匹配到仅指定了相应Scheme且没有指定Type的IntentFilter。实际的例子有如果一个Intent是想要发邮件,或是打电话,它们的Intent是类似这样的:"mailto:someone@sb.c<wbr>om</wbr>"和"tel:1234567"。换句话说,这些Uri本身就是数据,而不再是一个指向数据的地址。比如:Phone中的Dialer就有如下的IntentFilter:

  1. <intent-filter>
  2. <actionandroid:name="android.intent.action.CALL"/>
  3. <categoryandroid:name="android.intent.category.DEFAULT"/>
  4. <dataandroid:scheme="tel"/>
  5. </intent-filter>

再如,要处理SD状态变化的IntentFilter:

  1. <intent-filter>
  2. <actionandroid:name="android.intent.action.MEDIA_MOUNTED"/>
  3. <actionandroid:name="android.intent.action.MEDIA_UNMOUNTED"/>
  4. <actionandroid:name="android.intent.action.MEDIA_SHARED"/>
  5. <actionandroid:name="android.intent.action.MEDIA_REMOVED"/>
  6. <actionandroid:name="android.intent.action.MEDIA_EJECT"/>
  7. <categoryandroid:name="android.intent.category.DEFAULT"/>
  8. <dataandroid:scheme="file"/>
  9. </intent-filter>

再如,要处理Package状态变化的IntentFilter:

  1. <intent-filter>
  2. <actionandroid:name="android.intent.action.PACKAGE_ADDED"/>
  3. <actionandroid:name="android.intent.action.PACKAGE_REMOVED"/>
  4. <actionandroid:name="android.intent.action.PACKAGE_DATA_CLEARED"/>
  5. <categoryandroid:name="android.intent.category.DEFAULT"/>
  6. <dataandroid:scheme="package"/>
  7. <intent-filter>

但是注意,对于想对数据进行操作的Intent,最好不要只指定Uri,而不指定类型。因为如果这样做通常会匹配到一大堆
c. 如果一个Intent只指定了Type,但是没有指定Uri,它只能匹配到只指定了相应Type且没有指定Scheme的IntentFitler
d. 如果一个Intent即有Uri又有Type,那么它会匹配上:1).Uri和Type都匹配的IntentFilter;2).首先Type要匹配,另外如果Intent的Uri是content:或file:,且IntentFilter没有指定Scheme的IntentFilter。因为对于Android来讲content和file这二种Scheme是系统最常见也是用的最多的,所以就当成缺省值来对待。
另外需要注意,Type,因为是MimeType,所以是允许使用通配符的,比如'image/*',能匹配上所有以'image'为开头的类型,也说是说能匹配上所有的图像。

根据Data匹配的例子

假如系统中有四个Activity,A的IntentFilter是这样子的:
  1. <activity...>
  2. <intent-filter>
  3. <actionandroid:name="android.intent.action.SEND"/>
  4. <categoryandroid:name="android.intent.category.DEFAULT"/>
  5. <dataandroid:scheme="content"android:mimeType="image/*"/>
  6. </intent-filter>
  7. </activity>
这表明A可以发送一切图片类型,并且内容必须是由ContentProvider提供的,也就是Uri必须是以"content://"开头的
而另外一个Activity B是这样子声明的:
  1. <activity...>
  2. <intent-filter>
  3. <actionandroid:name="android.intent.action.SEND"/>
  4. <categoryandroid:name="android.intent.category.DEFAULT"/>
  5. <dataandroid:scheme="file"android:mimeType="image/*"/>
  6. </intent-filter>
  7. </activity>
这表明B可以发送一切图片,但内容必须是单独的一个文件,也就是Uri必须是由"file://"开头的
还有一个C是这样子声明的:
  1. <activity...>
  2. <intent-filter>
  3. <actionandroid:name="android.intent.action.SEND"/>
  4. <categoryandroid:name="android.intent.category.DEFAULT"/>
  5. </intent-filter>
  6. lt;/activity>
这表明C只能接收那些没有指定任何Uri和Type的Action是SEND的Intent。
分享到:
评论

相关推荐

    Android使用Intent和Intentfilter进行通信

    总结起来,Intent和IntentFilter是Android组件间通信的关键桥梁。Intent负责携带请求和数据,IntentFilter则负责匹配和筛选Intent,确保正确的组件得到请求。理解并熟练运用这两者,对于开发高效、灵活的Android应用...

    Intent和IntentFilter

    Intent 和 IntentFilter 是 Android 应用程序开发中的两个核心概念,它们构成了组件间通信的核心机制。Intent 可以被理解为一个信息载体,用于在不同的应用程序组件之间传递消息,而 IntentFilter 是一种声明机制,...

    Android组件间通信–深入理解Intent与IntentFilter

    在Android开发中,Intent和IntentFilter是两个至关重要的概念,它们构成了Android组件间通信的核心机制。Intent作为消息载体,能够连接应用内的不同组件,如Activity、Service,甚至跨应用进行通信,实现组件级别的...

    Android组件间通信--深入理解Intent与IntentFilter

    在Android开发中,Intent和IntentFilter是实现组件间通信的关键机制。Intent作为消息载体,能够连接不同的应用组件,包括Activity、Service、BroadcastReceiver等,使得它们可以互相传递数据和触发操作。...

    组件通信与广播消息优秀文档.ppt

    组件通信是Android应用程序设计的核心部分,Intent作为Android系统中组件间通信的重要工具,扮演着连接各个组件的关键角色。本章将深入探讨Intent的原理、使用方法以及如何通过Intent实现组件间的交互,特别是启动...

    Android组件通信与广播消息

    在Android开发中,组件通信是应用之间相互协作的基础,而Intent是实现这一功能的核心工具。本章主要讨论了如何利用Intent进行组件间的通信以及广播消息的发送与接收。 6.1 Intent Intent是一个用于在Android组件间...

    intent使用源码

    Intent是Android系统中的一种核心机制,它用于在应用程序的不同组件之间传递消息,实现组件间的通信。Intent不仅可以启动新的Activity,还可以启动Service或者BroadcastReceiver。在深入理解Intent的使用源码之前,...

    android中Activity和Intent的关系

    Activity可以理解为用户界面的“窗口”,而Intent则扮演着不同组件间通信的“信使”角色。在这里,我们将深入探讨这两个组件及其之间的关系。 首先,Activity是Android应用程序的基本构建块,它代表了用户可以看到...

    android整合--intent

    在Android开发中,Intent是一种非常重要的组件间通信(IPC)机制。Intent主要用于启动或与其它应用程序组件进行交互,如活动(Activity)、服务(Service)、广播接收器(BroadcastReceiver)以及内容提供者...

    Android Intent

    Intent在Android应用程序开发中扮演着至关重要的角色,它是Android系统组件间通信(IPC)的主要机制。通过Intent,开发者能够启动其他Activity、Service,传递数据,甚至执行BroadcastReceiver的动作。在《Android...

    Android_Intent和Intent_Filter详解

    Android Intent是 Android 组件间通信的载体,它们之间的通信是通过 Intent 对象在不断传递实现的。Android 的三大核心组件 Activity、Service 和 BroadcastReceiver 都是通过 Intent 对象进行通信的。 Intent 的...

    Intent系统调用示例

    总结,Intent是Android中至关重要的组件通信工具,理解并熟练运用Intent对于开发高效、稳定的Android应用至关重要。通过IntentDemo项目的学习,开发者可以更深入地掌握Intent的工作原理和应用场景。

    Intent传递

    总结,Intent在Android开发中起着至关重要的作用,它是组件间通信的桥梁,无论是同一应用内还是跨应用,都能通过Intent实现数据和控制的传递。理解并熟练掌握Intent的使用,对于构建功能丰富的Android应用至关重要。

    android intent简介

    Intent是Android系统中的一个核心概念,它是一种消息传递机制,用于在不同的组件之间进行通信,如活动(Activity)、服务(Service)、广播...通过正确设置Intent的各个属性,可以实现组件间的有效通信,提升用户体验。

    Android+Intent机制实例详解.rar

    在Android应用开发中,Intent是一种强大的机制,它...无论是启动Activity、Service,还是进行组件间的通信,Intent都是不可或缺的桥梁。通过实例学习和实践,开发者可以更好地掌握Intent的使用,提升应用的用户体验。

    Andriod Intent使用代码举例

    Android Intent是Android系统中用于组件间通信的重要机制,它能够启动或启动新的Activity,传递数据,甚至触发Service。Intent在Android应用开发中扮演着至关重要的角色,使得应用程序的不同部分能够协同工作。以下...

    智能家居系统 Intent组件-教案.doc

    本教案将深入探讨Intent组件,包括其基本概念、显示意图与隐式意图的区别,以及IntentFilter的作用,帮助学生掌握这一核心技术。 1. Intent组件基础: Intent组件是一种消息对象,用于在Android系统中启动一个活动...

    安卓基本知识之Intent学习源代码

    总之,Intent是Android应用程序组件间通信的桥梁,理解和熟练使用Intent对于Android开发者来说至关重要。通过研究提供的源代码,你可以更深入地理解Intent在实际项目中的运用,从而提升你的Android开发技能。

Global site tag (gtag.js) - Google Analytics