有一个问题,在网上被频繁的问到,就是为什么自定义的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