1. BroadCastReceiver是什么?
2. 广播类型
1). 有序广播
2). 无序广播
3. 生命周期
4. 实现原理
5. 使用方法
6. 权限问题(安全性)
7. LocalBroadcast
8. 注意事项
BroadCastReceiver
BroadCastReceiver即广播接收器,用于监听/接收Android应用分发的各类广播并做出相应的响应。
应用场景:
- 监听系统事件:如开机广播,网络连接与断开,屏幕开启与关闭等
- 不同组件间通信(多个Activity/service,包括不同应用间)
- 多线程通信
广播类型
广播可分为无序广播和有序广播:
无序广播
无序广播即广播被发送后,BroadCastReceiver之间是无顺序,完全异步的,各个Receiver之间无关联。无序广播无法通过abortBroadcast终止,也无法使用setResult和getResult来传递处理结果。
无序广播直接通过Context.sendBroadcast()来发送。
有序广播
有序广播即广播发送后会按照优先级顺序被不同的广播接收器接收,优先级可以通过intent-filter的android:priority属性来设置,定义范围为-1000~1000,数值越大,优先级越高(如果优先级相同,1. 发送广播的进程会优先接收 2. 先注册的Receiver先接收)。
有序广播发送后,会被优先级最高的BroadCastReceiver接收,然后在处理完毕后依次被优先级较低的BroadCastReceiver接收,期间,BroadCastReceiver可以设置setResult将该广播的处理结果传递给下一个BroadCastReceiver,下一个Receiver通过getResult获取。有序广播被接收时,还可以通过abortBroadcast来终止,终止后,广播不会继续传递给其他Receiver。如:开发一个拦截短信的功能,我们将priority设置为1000,来最大限度的保证短信可以被我们的Receiver接收,在接收后,如果不想短信显示在系统短信列表中,我们可以abortBroadcast来终止传递,此时也就不会有短信提示音,也不会再短信收件箱中看到该短信。
有序广播需要通过Context.sendOrderedBroadcast来发送
粘性广播
其实将粘性广播与有序广播/无序广播放在一起讲并不是非常合适,因为对它们的定义并不是在同一个维度上。
粘性广播通过Context.sendStickyBroadcast()来发送,它与非粘性广播的区别是,在onReceiver中粘性广播不受10s限制(普通广播onReceiver在10s未处理完毕,会抛出ANR),粘性广播在10s后仍然存在,直至广播处理完毕。但需要注意的是,10s后,系统会将这个广播置为candidate状态,即可以被干掉的,当系统资源不足时,广播仍然可能会被丢弃。
使用粘性广播需要android.Manifest.permission.BROADCAST_STICKY权限
生命周期
广播的生命周期从调用开始到onReceiver执行完毕结束,需要注意的是,一般广播的生命周期都极短,需要在10s内处理完onReceiver中的所有工作,所以,一般不进行耗时长的工作,如果有耗时长的工作,应当通过Intent传递给Service进行处理。(注意,不要在onReceiver中开启线程进行耗时任务处理,否则,在10s后,该线程会变成空线程,从而导致任务的丢失。同样的,也不要使用bindService来绑定服务。)
值得注意的是,如果是在代码中动态注册的广播,如:在Activity注册,那么在Activity的onDestory中需要使用unregisterReceiver注销广播。
实现原理
在Android中,广播的出现是为了组件间的通信。其实在Android中,进程间通信有Binder,而同进程的通信方式就更多了,之所以使用广播,发送者与接受者都不需要知道对方的存在,这样带来的好处便是,系统的各个组件可以松耦合地组织在一起,这样系统就具有高度的可扩展性,容易与其它系统进行集成。
Android中的广播使用了观察者模式:基于消息发布/订阅模式的事件驱动模型。
广播模型中包含三个角色:
- 消息发布者(广播发布者)
- 消息中心(AMS,即Activity Manager Service)
- 消息订阅者(广播接收者)
- 广播接受者通过Binder机制在AMS中注册监听
- 广播发布者通过Binder机制向AMS发送广播
- AMS根据发送者的需求,在已注册表中获取到合适的广播接受者(根据Intent-filter,Permission)
- AMS将广播发送给合适的广播接受者的消息循环队列中
- 广播接受者通过消息循环获取到广播并回调onReceive()
整个广播发送与接收过程中,发送者与接收者是异步的,发送者不需要知道是否有接受者,也不需要知道接受者何时收到广播。
使用方法
广播的使用包括以下几个步骤:
- 自定义广播接收者:重写onReceiver()
- 注册广播接收者到消息中心(AMS):动态(代码中registerReceiver)或者静态注册(AndroidManifest.xml中申明)
- 定义及发送广播到消息中心(AMS):Context.sendBroadcast()、Context.sendOrderedBroadcast、Context.sendStickyBroadcast()
- AMS选择并发送广播给合适的广播接受者(根据Intent-filter,Permission)
- 广播接受者通过消息循环获取广播,并调用onReceive()进行处理
以上1~3步骤需要用户进行处理,4、5步骤由Android系统完成。
自定义广播接受者
广播接受者的自定义很简单,继承BroadcastReceiver基类并重写onReceive()即可:
// 继承BroadcastReceiver基类
public class MyBroadcastReceiver extends BroadcastReceiver {
// 复写onReceive()方法
// 接收到广播后,则自动调用该方法
@Override
public void onReceive(Context context, Intent intent) {
// 接收广播后的操作
}
}
注册广播接收者
广播接受者的注册包含静态注册和动态注册两种方式。
静态注册 即在AndroidManifest.xml中通过<receive>标签进行申明。该方式注册的广播为常驻广播,注册后,即使应用处于非运行状态,也可以接收到广播。
属性及实例:
<receiver
android:enabled=["true" | "false"]
android:exported=["true" | "false"] // 此broadcastReceiver能否接收其他App的发出的广播,默认值是由receiver中有无intent-filter决定:如果有intent-filter,默认值为true,否则为false
android:icon="drawable resource"
android:label="string resource"
android:name=".MyBroadcastReceiver " // 继承BroadcastReceiver子类的类名
android:permission="string" // 具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
android:process="string"> // BroadcastReceiver运行所处的进程 默认为与app同进程,可以指定独立的进程。注:Android四大基本组件都可以通过此属性指定自己的独立进程
<intent-filter>
<action android:name="android.net.conn.BOOT_COMPLETED" /> //用于指定此广播接收器将接收的广播类型,可以配置多条,这里给出的示例是android系统开机广播
</intent-filter>
</receiver>
动态注册 即在代码中通过registerReceiver(BroadcastReceiver receiver, IntentFilter filter)来动态注册广播,该方法包含两个参数,receiver即我们自己定义的MyBroadcastReceiver,IntentFilter即需要过滤的条件。动态注册的广播在应用停止运行后无法接收广播,比如在ActivityA中注册,则应当在ActivityA销毁前,使用unregisterReceiver(BroadcastReceiver receiver)来注销注册
MyBroadcastReceiver receiver=new MyBroadcastReceiver(); // 这里使用我们上面自定义的广播接受者
IntentFilter filter=new IntentFilter();
filter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); // 这里以网络连接状态变化为例
registerReceiver(receiver, filter);
定义及发送广播
广播的发送有三种方法sendBroadcast(),sendOrderedBroadcast()和sendStickyBroadcast()
示例如下:
Intent intent = new Intent();
intent.setAction("com.itlao5.broadcast");
intent.putExtra("data", "data");
sendBroadcast(intent);
权限问题(安全性)
看到这里,应该都很清楚如何发送及接受广播。但是在此过程中是否会存在一些问题,比如我发送的广播只想让指定的广播接收者接收,或者我只想接收指定发送者发送的广播,这些该如何实现呢?
在上面广播注册一节中,我们可以看到有一个permission属性,没错,我们可以通过指定权限来实现上面的需求。
- 如何发送给指定的广播接受者?
在AndroidManifest.xml中,我们可以配置
<permission android:name = "com.android.permission.XXX"/>
然后在发送广播时
sendBroadcast("com.android.XXX_ACTION", "com.android.permission.XXX");
此时,就只有在广播接收的应用中在AndroidManifest.xml中添加对应的XXX权限,才能正常接收到该广播。
<uses-permission android:name="com.android.permission.XXX">></uses-permission>
- 如何接收指定发送者的广播?
在这种情况下,需要在接受者app的<receiver> tag中声明一下发送者app应该具有的权限。
首先同上,在AndroidManifest.xml中定义新的权限SEND_XXX,例如:
<permission android:name="com.android.SEND_XXX"/>
然后,在接受者app的Androidmanifest.xml中的<receiver> tag里添加权限SEND_XXX的声明
<receiver android:name=".MyBroadcastReceiver" android:permission="com.android.permission.SEND_XXX"> <intent-filter> <action android:name="com.android.XXX_ACTION" /> </intent-filter> </receiver>
如此,该接受者便只能接收来自具有该SEND_XXX权限的应用发出的广播。要发送该广播,只需在发送者app的AndroidManifest.xml中也声明使用该权限即可,如下:
<uses-permission android:name="com.android.permission.SEND_XXX"></uses-permission>
LocalBroadcast
既然是说的广播,那么这里仅顺便提及一下LocalBroadcast。LocalBroadcast是本地广播,它的出现是为了应用内的广播传递。准确的说,LocalBroadcast与Broadcast不同,BroadCast基于Binder机制,而LocalBroadcast则是基于Handler机制。其机制的不同,导致他们的应用场景也不同,LocalBroadcast消耗资源少,安全性也相对来说更有保障,所以在应用内,更推荐使用LocalBroadcast。
这里简单提一下,有兴趣的可以看这篇博文:Android开发之局部广播的使用——LocalBroadcast
注意事项
- 动态注册的广播,在不需要使用时或者载体即将销毁时进行注销,即每一个registerReceiver需要有一个对应的unregisterReceiver
- 不要在广播接收器onReceive()中进行耗时操作,否则会引起ANR(10s)
- 不要在广播接收器onReceive()中开启异步任务,否则因为其生命周期的结束会出现空线程,导致任务丢失或者出现ANR等情况
- 耗时任务请开启service进行处理,且应当使用startService,而不应该使用bindService
- 应用内的广播尽量使用localBroadcast,因为其使用Handler,较Broadcast的Binder机制开销更小,且安全性更高
- 动态注册的广播优先度比静态注册高(当配置的优先级一致时),且可以控制其注册与注销,开销更小,所以能满足功能的情况下优先使用动态注册
- 如果接受不到自己发送的广播,请注意是否是因为权限问题