本文主要简单的讨论下Android中广播发送与注册的多种方式
注册广播接收者有两种方式,动态与静态:
1,动态注册:在Activity的onCreate方法中或根据需要调用register()方法注册即可动态注册一个广播接收者,在代码中有关于其特点的简单注释,如下
private void register() {
ReceiverTool rt = new ReceiverTool();
IntentFilter filter = new IntentFilter();
filter.addAction("com.xiaomo.view.broadcast01");
registerReceiver(rt, filter);
}
/**
* 动态注册 -- 接收广播
* 每次接收广播使用的是同一个接收实例,并不会每次都创建一个实例
* @author Administrator
*
*/
class ReceiverTool extends BroadcastReceiver {
public ReceiverTool () {
// Log.i(Constant.TAG, "创建了ReceiverTool的一个实例");
}
@Override
public void onReceive(Context context, Intent intent) {
Log.i(Constant.TAG, "" + this.hashCode());
Log.i(Constant.TAG, "接到到广播, ACTION = " + intent.getAction());
}
}
2,静态注册,自定义一个class extends BroadcastReceiver,然后在mainfest.xml中使用<receiver...>标签的形式注册,特性同样注释在代码中:
/**
* 静态注册(mainfest.xml) -- 接收广播
* 每次接收到相应的广播,都会创建一个接收器的实例,这一点与动态注册不同
* 之所以这样设计,个人认为实属无奈,因为在mainfest.xml中注册了一个接收者,相当于一个标记
* 但系统无法知道什么时候会接收到对应的广播
* 1,系统在扫描到配置文件中注册了接收者时,就为其创建对象 (不知道什么时候会接收到广播)
* 2,系统在该接收者接收到广播时,第一次为其创建对象 (不知道什么时候会再次接收到广播)
* 如果这两种情况下,都在内存中保存这个接收者对象,那么就会出现对应的括号中场景
* 并且一直保存在内存中,明显占内存,浪费内存资源(一个接收者无关紧要,但若一个应用中有10, 100..个静态广播接收者,你懂的!)
* 所以不能采用这样的设计,而采用每次接收到广播,就为其创建对象,用完就清掉
* 而且在处理接收广播过程中,Android还加上了超时限制(ANR异常,因为BroadcastReceiver默认运行在UI主线程中)
* @author Administrator
*
*/
public class StaticReceiverTool extends BroadcastReceiver{
public StaticReceiverTool() {
// Log.i(Constant.TAG, "创建了StaticReceiverTool的一个实例");
}
@Override
public void onReceive(Context context, Intent intent) {
// Log.i(Constant.TAG, "" + this.hashCode());
Log.i(Constant.TAG, "StaticReceiverTool接到到广播, ACTION = " + intent.getAction());
}
}
mainfest.xml:
<receiver android:name="com.xiaomo.view.broadcast.StaticReceiverTool">
<intent-filter>
<action android:name="com.xiaomo.view.broadcast01"></action>
</intent-filter>
</receiver>
广播接收者可以接收系统自带的广播,也可以接收自定义的广播
那么相应的,Intent中的action可以自定义也可以是使用系统相应服务特定的action
自定义的广播发送方式一般如下:
Intent intent = new Intent("com.xiaomo.view.broadcast01");
sendBroadcast(intent);
// sendBroadcast(intent);连续发送两次,那么接收者就会连续接收两次同样的广播
对于多个广播接收者接收同一个广播,会涉及到优先级priority的概念,下面再定义一个broadcastReceiver(动态与静态的区别上面已经说过,这里采用静态的方式):
public class StaticReceiverTool02 extends BroadcastReceiver {
public StaticReceiverTool02() {
}
@Override
public void onReceive(Context context, Intent intent) {
Log.i(Constant.TAG,
"StaticReceiverTool02接到到广播, ACTION = " + intent.getAction());
// 接收non-ordered广播时,如果调用方法终止广播
// 会报异常:BroadcastReceiver trying to return result during a non-ordered
// broadcast
// this.abortBroadcast();
}
其对应的xml中得配置:
<receiver android:name="com.xiaomo.view.broadcast.StaticReceiverTool02">
<intent-filter android:priority="10">
<action android:name="com.xiaomo.view.broadcast01"></action>
</intent-filter>
</receiver>
注意StaticReceiverTool02这个receiver的配置中加了优先级android:priority="10",那么使用上面的代码发送广播后,Log中的状态为:
StaticReceiverTool02接到到广播, ACTION = com.xiaomo.view.broadcast01
StaticReceiverTool接到到广播, ACTION = com.xiaomo.view.broadcast01
因为StaticReceiverTool02的优先级priority = 10 比StaticReceiverTool的0高(如果不加优先级设置,两者几乎同时收到广播,没有固定的顺序)
在android系统中默认的优先级 = 0, 优先级的范围为-1000 ~ 1000,最高的优先级为1000.
如果想终止广播,可以调用abortBroadcast();方法,但上面的示例中无论在哪个接收者中调用,都会出现异常(见code注释),这涉及到android系统中几种不同的广播发送方式,而若使用上面代码中的发送方式sendBroadcast(intent),则接收者在处理过程中是不能终止广播的继续传递的,这个在源码中明确说明。
接下来讨论下广播的几种发送方式:
1, 就是上面说到的常用方式:sendBroadcast(intent);
2, 有序广播 sendOrderedBroadcast(intent, "com.xiaomo"); (有序的广播可以使用abortBroadcast()终止广播)
第二个参数为权限控制,无权限就写null即可
当然若加了权限,则必须在maifest.xml中申明权限且设置接收权限
<uses-permission android:name="com.xiaomo" />
<permission android:name="com.xiaomo" android:protectionLevel="normal"></permission>
这样才可以接收到广播,此时若在StaticReceiverTool02的onReceiver()中加入代码this.abortBroadcast();
那么StaticReceiverTool就接收不到这个广播了.
3,sendStickyBroadcast(intent);
这个不涉及到自定义权限,但是需要在配置文件中设置使用权限:<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
这种方式也同样不能在接收过程中,终止广播.
这种方式的特殊之处在于,广播发送出去后,即使没有对应的接收者,内存中也会缓存这个广播,在未删除之前,一旦有相应的接收者注册,那么这个接收者就会收到这个广播
还使用上面的例子,把action改一下即可(此段代码在activity的oncreate(...)中):
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
register();
}
}, 30000);
// new Handler().postDelayed(new Runnable() {
// @Override
// public void run() {
//
// register();
// }
// }, 20000);
Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.xiaomo.view.broadcast02");
sendStickyBroadcast(intent);
Log.i(Constant.TAG, "广播发送完毕");
}
});
把上面register()方法中action的string值改成对应的02(额,主要是不想动配置文件了,不然好几个接收者接收同一个广播,对比的不清楚)
filter.addAction("com.xiaomo.view.broadcast02");
运行后,先点击button发送广播,可以发现log中只显示了“广播发送完毕”,等待大约30秒后,会显示“接收到广播。。。。”
即通过sendStickyBroadcast(intent)发送的广播先缓存在内存中,30秒后子线程执行注册广播接收者的代码,然后这个接收者就会立即接收到...broadcast02这个广播
当然也可以从内存中将这个缓存的广播删除:
@Override
public void onReceive(Context context, Intent intent) {
removeStickyBroadcast(intent);//删除在内存中的广播
Log.i(Constant.TAG, "" + this.hashCode());
Log.i(Constant.TAG, "接到到广播, ACTION = " + intent.getAction());
}
这样其他可以接收到这个广播的接收者就不会接收到这个广播了(当然当前的receiver得比其他receiver先接收到这个广播, 顺序问题又涉及到上面的有序/无序,优先级的问题了)
4,还有一种比较蛋疼的方式:sendStickyOrderedBroadcast(Intent intent,
BroadcastReceiver resultReceiver,
Handler scheduler, int initialCode, String initialData,
Bundle initialExtras)
。。。哈哈,好吧,我承认,这个我看了一眼,就没看过第二眼(当然也从来没见过哪里使用这种方式发送广播的....)