Android——事件过滤策略和 IntentFilter

来源:互联网 发布:lua 5.2 for windows 编辑:程序博客网 时间:2024/05/16 13:58

有一个问题,在网上被频繁的问到,就是为什么自定义的Receiver总是无法接收到SD卡插拔的事件。而此问题大部分情况下可以通过增加一句代码解决: filter.addDataScheme(“file”); // filter是IntentFilter对象

那么为什么增加这句代码就可以解决了呢? 在网上看到一遍比较通俗易懂的文章,觉得不错,摘抄过来做个笔记,供以后复习用。

SD卡插拔的广播事件示例代码

package com.silenceburn;  import android.app.Activity;  import android.content.BroadcastReceiver;  import android.content.Context;  import android.content.Intent;  import android.content.IntentFilter;  import android.os.Bundle;  import android.util.Log;  public class SdCardTester extends Activity {      BroadcastReceiver mReceiver;      @Override      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.main);          Log.i("myLoger"," onCreate ......");          mReceiver = new BroadcastReceiver() {              @Override              public void onReceive(Context context, Intent intent) {                              Log.i("myLoger", "Component: " + intent.getComponent());                  Log.i("myLoger", "Aciton: " +  intent.getAction());                  Log.i("myLoger", "Categories: " +  intent.getCategories());                  Log.i("myLoger", "Data: " + intent.getData());                  Log.i("myLoger", "DataType: " + intent.getType());                  Log.i("myLoger", "DataSchema: " + intent.getScheme());                  Log.i("myLoger"," Receive SDCard Mount/UnMount!");              }          };          IntentFilter filter = new IntentFilter();          filter.addAction(Intent.ACTION_MEDIA_MOUNTED);          filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);          filter.addDataScheme("file");          registerReceiver(mReceiver, filter);      }      @Override      protected void onDestroy() {          super.onDestroy();          unregisterReceiver(mReceiver);      }  }  

运行结果:
这里写图片描述

通过日志输出我们可以得知挂载SD卡事件的 Componet 是null ,因此它是一个隐式事件。因此能否送达,需要看事件的 action, data , category 和 IntentFilter是否匹配。

1、ACTION是 android.intent.action.MEDIA_MOUNTED,和我们定义的IntentFilter的 filter.addAction(Intent.ACTION_MEDIA_MOUNTED); 语句相匹配。
因此action部分是匹配的。

2、Categories是null,而我们定义的 IntentFilter 也没有使用addCategory方法增加category定义,null == null,因此 categories也是匹配的。

3、action, data , category 中的两个要素已经匹配,那么能否匹配成功的关键,就是看data是否匹配了。

data匹配规则

首先务必认识到,data是一个相对复杂的要素。 data由URI来描述和定位,URI由三部分组成:

scheme://host:port/path 模式://主机:端口/路径

例如我们截获的挂载SD卡事件,它的data的URI是 file:///mnt/sdcard
其中模式部分是 file, 主机:端口部分是空的, path部分是mnt/sdcard
此外在事件中,还可以设置data的MIME类型,作为事件的datatype属性。

首先明确一个匹配原则,就是对于URI的匹配,只比较filter中声明的部分。
部分匹配原则:只要filter中声明的部分匹配成功,就认为整个URI匹配成功。

举例来说, content://com.silenceburn.SdCardTester:1000/mydata/private/
和filter定义为 content://com.silenceburn.SdCardTester:1000/ 是可以匹配的。
注意filter中并没有定义path部分,但是依然可以匹配成功,因为filter不声明的部分不进行比较。换句话讲,任何符合content://com.silenceburn.SdCardTester:1000/的事件,无论path是什么,都可以匹配成功。

接下来是真正的data部分的,也就是URI的匹配规则如下:
1. 如果data的URI和datatype为空,则 filter 的URI和type也必须为空,才能匹配成功。
2. 如果data的URI不为空,但是datatype为空,则 filter 必须定义URI并匹配成功,且type为空,才能匹配成功。
3. 如果data的URI为空,但是datatype不为空,则 filter 必须URI为空,定义type并匹配成功,才能匹配成功。
4. 如果data的URI和data都不为空,则 filter 的URI和type都必须定义并匹配成功,才能匹配成功。

对于URI部分,有一个特殊处理,就是即使filter没有定义URI,content和file两种URI也作为既存的URI存在。
(举个例子,对于 content 和 file 两种模式的data,只要filter定义的datatype可以和事件匹配,就认为匹配成功,filter不需要显式的增加 content 和 file 两种模式,这两种模式内置支持 )

针对SD卡插拔事件的分析

SD卡插拔是一个隐式事件,而且 action 和 category 部分和我们的 filter 均可以匹配成功。

其data部分的URI为 file:///mnt/sdcard ,datatype为 null ,因此应用上面比较规则中的 2 号规则,分析如下:
1、我们的filter中没有使用 addtype 方法 ,因此 filter 的type为空, datatype部分匹配成功。
2、data的URI为file:///mnt/sdcard ,我们使用filter.addDataScheme(“file”); 语句定义 schema 为 file,根据部分匹配规则,data匹配成功。

至此,整个事件匹配成功,至此,我们就明白了,为什么必须添加 filter.addDataScheme(“file”); 语句才能收到事件!

我们可以尝试把 filter.addDataScheme(“file”); 后面增加语句:

filter.addDataPath("mnt/sdcard",PatternMatcher.PATTERN_LITERAL);  

依然可以匹配成功,收到SD卡插拔事件。因为这样就组成了一个URI的完全匹配。

文章转载于:http://blog.csdn.net/silenceburn/article/details/6083375

1 0