谨以文章记录学习历程,如有错误还请指明。
前言
我们上学时都有过这样的经历,当我们在火车站列车候车室中等待时,每当有某次列车开始检票或者进站上车时,就会播放通知来告知在候车室等待的人们该消息。
为了便于进行系统级别的消息通知,Android引入了一套类似的广播机制,然而比上述情景要灵活得多。此文将对Android广播机制的方方面面做出详尽的介绍。
Android广播机制简介
前面我们提到,Android的广播机制更加的灵活,这是因为Android允许每个应用只对自己感兴趣的广播进行注册,这样该程序就只会收到自己所关心的广播内容。
Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver
指的就是广播接收者(广播接收器)。
应用场景
同一应用具有多个进程的不同组件之间的消息通信
- 不同应用间的组件之间的消息通信
- 与Android系统在特定情况下的通信
- 如:系统开机,网络变化等
以上只说明适合广播机制的应用场景,还有一些场景理论上可以使用,但是实际开发没有人这么做:
同一应用内同一组件的消息通信:显然扩展变量的作用域、接口回调、
Handler-Message
等方式都能更简单的实现。同一应用内的不同组件之间的消息通信(单个进程):对于简单的的情况,依靠接口的回调方式就可解决;而较为复杂的情况,更推荐直接使用
EventBus
等。
实现原理
设计模式与模型
Android中的广播使用了观察者模式,模型为 基于消息的发布/订阅事件模型。
从设计模式上讲,广播的发送者和接收者极大程度的解耦,使得系统方便集成,容易扩展
模型成员:
- 消息发布者(广播发布者)
- 消息订阅者(广播接收者)
- 消息中心(AMS,Activity Manager Service,一个Android系统中极其重要!的成分,以后我们会详细讲解)
此处我们扩展一下,观察者模式和发布订阅模式的关系
发布订阅模式属于广义上的观察者模式
前者时最常用的一种观察者模式的实现,且从解耦和重用角度上看更优于典型的观察者模式发布订阅模式加入消息中心,实现发布者和订阅者的解耦:
- 在观察者模式中,观察者需要直接订阅目标事件,在目标发出内容改变的事件后,直接接收事件并作出响应。
- 在发布订阅模式中,多了一个消息中心,一方面从发布者接收事件,另一方面向订阅者发布事件,订阅者需要从消息中心订阅事件。以此避免发布者和订阅者之间产生依赖关系。
实现流程
- 广播接收者
BroadcastReceiver
通过Binder
机制向AMS(Activity Manager Service
)进行注册; - 广播发送者通过
binder
机制向AMS发送广播; - AMS查找符合相应条件(
IntentFilter
/Permission
等)的BroadcastReceiver
- AMS将广播发送到上述符合条件的
BroadcastReceiver
相应的消息循环队列中 BroadcastReceiver
通过消息循环执行拿到此广播,回调BroadcastReceiver
中的onReceive()
方法。
广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到。
BroadcastReceiver
自定义BroadcastReceiver
- 继承基类
BroadcaseReceiver
实现抽象方法
onReceive(context, intent)
- 收到广播后,会自动回调
onReceive(..)
方法 - 通常,
onReceive(..)
方法会涉及到与其他组件的交互,如发送Notification
,启动service
等 - 默认情况,
BroadcaseReceiver
运行在UI线程,因此,onReceive(..)
方法不能执行耗时操作,否则ANR
- 收到广播后,会自动回调
简单的自定义Demo:
MyBroadcastReceiver.java
//继承BroadcastReceiver基类
public class MyBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "MyBroadcastReceiver";
@Override
public void onReceive(Context context, Intent intent) {
StringBuilder sb = new StringBuilder();
sb.append("Action: " + intent.getAction() + "\n");
sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
String log = sb.toString();
Log.d(TAG, log);
Toast.makeText(context, log, Toast.LENGTH_LONG).show();
}
}
BroadcastReceiver注册类型
1. 静态注册
- 在
AndroidManifest.xml
文件中通过<receiver>
进行注册 - 规则及实例说明:
<receiver
//BroadcastReceiver子类的类名
android:name="string"
//是否使用该BroadcastReceiver
android:enabled=["true" | "false"]
//此broadcastReceiver能否接收其他App的发出的广播
//其默认值是由receiver中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"