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来启动,但你必须知道它们的名字。 设置组件的名字的方法有: 复制代码 代码如下: public Intent setComponent(ComponentName component); public Intent setClass(Context packageContext, Class<?> cls); public Intent setClassName (Context packageContext, String className); public Intent setClassName (String packageName, String className);
事实上虽然设置的方法有这么多,但Intent内部标识目标组件的属性只有一个Component,所以这么设置方法的目的也只是设置目标组件的Component,事实上有这么多的方法原因在于ComponentName的构造是多重载了的。在解析Intent时,系统也是取得这个Component属性,然后去启动它。 ComponentName Intent.getComponent(); 对于应用程序内部启动Activity通常是这样子设置Intent: 复制代码 代码如下: Intent i = new Intent(); // Select one of them i.setComponent(new ComponentName(getApplication(), ViewStubDemoActivity.class)); i.setComponent(new ComponentName(getApplication(), ViewStubDemoActivity.class.getName())); i.setComponent(new ComponentName(getApplication().getPackageName(), ViewStubDemoActivity.class.getName())); i.setClass(getApplication(), ViewStubDemoActivity.class); i.setClassName(getApplication(), ViewStubDemoActivity.class.getName()); i.setClassName(getApplication().getPackageName(), ViewStubDemoActivity.class.getName()); startActivity(i);
因为应用程序内部的组件类,都是可以访问到的,所以要尽可能少写字串常量,以减少拼写错误,如果一定要使用包名和类名,也要注意,类名必须是全称,也就是从包名开始,如“com.hilton.networks.WifiManagerActivity"。 但是对于外部应用程序的Activity,通常只能通过以下方法: 复制代码 代码如下: Intent i = new Intent(); // select one of them i.setComponent(new ComponentName("com.hilton.networks", "com.hilton.networks.WifiManagerActivity")); i.setClassName("com.hilton.networks", "com.hilton.networks.WifiManagerActivity"); startActivity(i);
那么如果uri是数据库内容,它会匹配到A,如果它是一个文件,会匹配到B。但无论是content还是file都会匹配到D,因为它能处理以任何形式存储的图片。但始终不会匹配到C,因为C没有声明Data字段,所以不会匹配上。 所以,通常想把组件作为系统公用接口时都是这样子来写: 复制代码 代码如下: <activity ...> <intent-filter> <!-- implement public actions such as View, Edit, Pick or Send --> <action android:name="android.intent.action.SEND" /> <!-- never forget default category, otherwise your activity never receives intents --> <category android:name="android.intent.category.DEFAULT" /> <!-- specify mimeType to constrain data type, receive data from both content provider and file --> <data android:mimeType="image/*" /> <!-- specify scheme to constrain data source, if necessary --> <data android:shceme="http" /> </intent-filter> </activity>