Android - Intent与IntentFilter

Intent类的注释:


一个intent是要被执行的操作的一种抽象的描述,结合Context.Java类中定义的几个方法 ——

[java]  view plain  copy
 
  1. <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">public abstract void startActivity(Intent intent);  
  2. public abstract void sendBroadcast(Intent intent);  
  3. public abstract ComponentName startService(Intent service);  
  4. public abstract boolean bindService(Intent service, ServiceConnection conn, int flags);</span></span>  

它可以被用来和对应的组件进行通信,是组件间进行通信的中介,其中,intent最重要的作用是用来启动Activity

上述提到的Context.java类中定义的几个方法中,都有一个Intent对象的参数,于是,先来看Intent类的构造方法
Intent类常用的构造方法有如下几个:
[java]  view plain  copy
 
  1. <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">public Intent() { }     
  2.       
  3. public Intent(String action) {  
  4.     setAction(action);    
  5.     /*public Intent setAction(String action) { 
  6.         mAction = action != null ? action.intern() : null; 
  7.         return this; 
  8.     }*/  
  9. }  
  10.       
  11. public Intent(String action, Uri uri) {  
  12.     setAction(action);  
  13.     mData = uri;  
  14. }     
  15.           
  16. public Intent(Context packageContext, Class<?> cls) {  
  17.     mComponent = new ComponentName(packageContext, cls);  
  18. }</span></span>  
在这些构造方法中,主要的操作就是为Intent的成员变量赋值,Intent类的成员变量主要有:
[java]  view plain  copy
 
  1. <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">private String mAction;  
  2. private Uri mData;  
  3. private ComponentName mComponent;  
  4. private int mFlags;  
  5. private ArraySet<String> mCategories;  
  6. private Bundle mExtras;  
  7. ... ...</span></span>  
事实上,除了在构造函数中为这些成员变量赋值,还可以利用下列方法为对应的成员变量赋值:
[java]  view plain  copy
 
  1. <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">public Intent setAction(String action) { }      
  2. public Intent setData(Uri data) { }   
  3. public Intent setComponent(ComponentName component) { }   
  4. public Intent setClassName(Context packageContext, String className) { }      
  5. public Intent setFlags(int flags) { }     
  6. public Intent addCategory(String category) { }</span></span>  
那么,Intent类的这些成员变量分别都是什么意思呢?

Intent的Component属性

Intent类的setComponent(ComponentName component)方法用于设置Intent的Component属性:
[java]  view plain  copy
 
  1. <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">public Intent setComponent(ComponentName component) {  
  2.     mComponent = component;  
  3.     return this;  
  4. }</span></span>  
该方法接收一个ComponentName 类型的参数,ComponentName 类有三个构造函数:
[java]  view plain  copy
 
  1. <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">public ComponentName(String pkg, String cls) {    }     
  2. public ComponentName(Context pkg, String cls) {  }    
  3. public ComponentName(Context pkg, Class<?> cls) {  }</span></span>  
由这三个构造函数可知,指定包名和类名便可确定一个组件,事实上,设置Component属性的目的就是指定一个Intent将要被用来与哪个组件进行通信。以下3句代码创建了一个Intent对象,并指定了它的Component属性:
[java]  view plain  copy
 
  1. <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">ComponentName  comp =new ComponentName(this,OtherActivity.class);  
  2. Intent  mIntent = new  Intent();  
  3. mIntent.setComponent(comp); </span></span>  
除了上述这种方式,还可以这样做:
[java]  view plain  copy
 
  1. <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">Intent  mIntent = new  Intent(this,OtherActivity.class);</span></span>  
也可以这样做:

[java]  view plain  copy
 
  1. <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">Intent  mIntent = new  Intent();  
  2. mIntent.setClassName(this,OtherActivity);   
  3. // 我们看到setClassName(Context packageContext, String className)方法  
  4. // 内部调用了mComponent = new ComponentName(packageContext, className);  </span></span>  

上文中,我们提到,设置Component属性的目的就是指定一个Intent将要被用来与哪个组件进行通信。所以,当一个Intent对象设置了它的Component属性的值,这个Intent就被称作显示Intent(显示意图)。显式Intent应用场合比较狭窄, 多用于启动本应用中的Component,因为这种方式需要提前获知目标组件类的全类名。

Intent的Action属性

mAction是Intent的一个String类型的成员变量,它代表某一种特定的操作。 Intent类预定义了一些action常量, 开发者可以自定义action, 自定义的action应该以application的包名作为前缀, 然后附加特定的大写字符串, 
如"android.intent.action.CALL"。
Intent类的setAction(String action)方法用于设置Intent的Action属性:

以下是Intent类中预定义的部分action:
ACTION_CALL--目标组件为activity,代表拨号动作;
ACTION_EDIT--目标组件为activity,代表向用户显示数据以供其编辑的动作;
ACTION_MAIN--目标组件为activity,表示作为task中的初始activity启动;
ACTION_BATTERY_LOW--目标组件为broadcastReceiver,提醒手机电量过低;
ACTION_SCREEN_ON--目标组件为broadcast,表示开启屏幕。

Intent的Category属性

mCategories是Intent的一个ArraySet<String>类型的成员变量,category属性用于指定一些目标组件需要满足的额外条件。 Intent对象中可以包含任意多个category属性。 Intent类也预定义了一些category常量, 开发者也可以自定义category属性。
Intent类的addCategory(String category)方法为Intent添加Category属性:
[java]  view plain  copy
 
  1. <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">public Intent addCategory(String category) {  
  2.     if (mCategories == null) {  
  3.         mCategories = new ArraySet<String>();  
  4.     }  
  5.     mCategories.add(category.intern());  
  6.     return this;  
  7. }</span></span>  
以下是Intent类中预定义的部分category:
CATEGORY_HOME--表示目标activity必须是一个显示homescreen的activity;
CATEGORY_LAUNCHER--表示目标activity可以作为task栈中的初始activity,常与ACTION_MAIN配合使用;
CATEGORY_GADGET--表示目标activity可以被作为另一个activity的一部分嵌入。

Intent的Extra属性

通过Intent启动一个component时, 经常需要携带一些数据过去。这个工作主要由Intent的Extra属性来完成,它对应的是Intent 的Bundle类型的成员变量 mExtras ,换句话说,这就是给成员变量 mExtras 赋值的问题。可以采用两种方式:
A、使用putExtra()方法,该方法具有多个重载方法,如putExtra(String name, byte value)、putExtra(String name, String value)、putExtra(String name, byte[] value)、putExtra(String name, float[] value)等,以putExtra(String name, boolean value)为例:
[java]  view plain  copy
 
  1. <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">public Intent putExtra(String name, boolean value) {  
  2.     if (mExtras == null) {  
  3.         mExtras = new Bundle();  
  4.     }  
  5.     mExtras.putBoolean(name, value);  
  6.     return this;  
  7. }</span></span>  
B、
[java]  view plain  copy
 
  1. <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">Bundle  mBundle = new Bundle();  
  2. mBundle.putBoolean(... ...);  
  3. mIntent .putExtras(mBundle);      
  4. /*public Intent putExtras(Bundle extras) { 
  5.     if (mExtras == null) { 
  6.         mExtras = new Bundle(); 
  7.     } 
  8.     mExtras.putAll(extras); 
  9.     return this; 
  10. }*/</span></span>  
可以看到,两种方式实质是一样的,第二种的扩展性相对来说更好一些。

Intent的Data属性

Data属性对应的是 Intent 的 Uri 类型的成员变量 mData ,它用来指定所操作数据的URI。data经常与action配合使用, 如果action为ACTION_EDIT,data的值应该指明被编辑文档的URI; 如果action为ACTION_CALL,data的值应该是一个以"tel:"开头并在其后附加号码的URI; 如果action为ACTION_VIEW,data的值应该是一个以"http:"开头并在其后附加网址的URI ... ...
Intent类的setData(Uri data)方法用于设置 mData 的值,setType()方法用于设置data的MIME类型,setDataAndType()方法可以同时设定两者。

上述简要总结了Intent的常用属性,其中,为Component属性赋了值的Intent被称为显示Intent,Component属性的目的就是指定一个Intent将要被用来与哪个组件进行通信。
那如果Component属性没有被赋值呢?
Component属性没被赋值的Intent被称为隐示Intent,这种情况下,一个Intent将被用来与哪个组件进行通信呢?下面来讲IntentFilter

IntentFilter类的注释:
... ... IntentFilter objects are often created in XML as part of a package's de>AndroidManifest.xmlde> file, using de>intent-filterde> tags ... ...

IntentFilter通常被使用在应用程序的清单文件中,在一个组件内部以intent-filter节点的形式来定义,采用白名单管理,即只列出愿意接受的intent

那么,这里就涉及到一个匹配规则的问题。

Android组件可以有一个或多个IntentFilter,每个IntentFilter之间相互独立,一个intent只需要和其中一个IntentFilter匹配上即,而只有一个intent的action、Category 和data都同时和某个IntentFilter相匹配,才能认为这个intent和IntentFilter相匹配

一、action的验证:

只要intent设置的action全部来源于某个IntentFilter当中,则匹配成功,需要注意的是,如果intent中没有指定action,那么匹配失败。而且区分大小写。

二、Category的验证:如果Intent中的Category集合是Intentfilter中Category的集合的子集时,Intent能通过检查。另外,系统在调用startActivity和startActivityForResult时默认会给所有 Intent 配置 “android.intent.category.DEFAULT” category。所以只要是想接收一个隐式 Intent 的 IntentFilter都应该包括 "android.intent.category.DEFAULT" category,不然将导致 Intent 匹配失败。

三、data的验证(主要参考任玉刚的《Android开发艺术探索》一书,谢谢作者):

和action类似,如果过滤规则中定义了data,那么intent中也需要有可匹配的data,data的结构为:

Android - Intent与IntentFilter_第1张图片

data由两部分组成,mimeType和URI,mimeType指数据类型,比如image/jpeg、audio/mpeg4-generic和video/*等,可以表示图片、文本、视频等不同的媒体格式,而URI的结构为:

scheme://host:port/path/pathPrefix/pathPattern,如:

content://com.example.project:200/folder/subfolder/etc 和 http://www.baidu.com:80/search/info

scheme:URI的模式,如http、file、content等,无scheme则URI无效

host:URI的主机名,比如www.baidu.com,无host则URI无效

path、pathPrefix和pathPattern表示路径信息

如果要为intent指定完整的data,必须调用setDataAndType方法,不能先调用setData再调用setType,因为这两个方法会清除对方已经设置的值,如:intent.setDataAndType(Uri.parse("http://abc"),"video/mpeg");

最后,当我们通过隐式intent启动一个activity时,需要先做一下判断,看是否有activity能够匹配我们的intent,判断的方法有两种:

采用PackageManager的resolveActivity或者Intent的resolveActivity,如果找不到会返回null。如果没有能匹配上的activity并且没有做判断的话,很可能发生ActivityNotFoundException。

... ... (未完待续)


你可能感兴趣的:(android)