一,什么是广播?
广播, 做为Android的四大组件之一,被用于发送和接收,来自系统或其它app的消息。其设计原理,是基于观察者模式来设计的,即先订阅,后接收。广播消息,并不受限于某一个单一进程。因此,它可以用于跨进程间的通信,在多个App之间传递消息。
二,广播的分类。
一,根据广播的订阅(定义)方式和生命周期,我们可以将广播归纳为2种类型,即静态广播与动态广播。
- 静态广播
静态注册的广播,在App被安装后,就自动被系统的PackageManager注册保存起来。这样,哪怕你的app,没有在运行,你的app也可以接收到广播。订阅这样一个广播,我们需要分2步实现。第1步,是创建一个类,继承自BroadcastReceiver。第2步,是在配置清单文件(AndroidManifest.xml)里面,进行配置第1步所创建的类。
- 创建继承自BroadcastReceiver的类,代码如下:
class MyBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// 当接收到广播时,这个方法会被调用,intent参数,包含了广播所附带的数据。
}
}
- 在AndroidManifest.xml里面,配置刚刚定义的类。这个标签,必须放在标签中间,与标签同层级。
<receiver android:name=".MyBroadcastReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
</intent-filter>
</receiver>
- 动态广播
动态广播,是指通过代码,来动态订阅。每一个动态广播,都绑定一个上下文Context,要么是某个Activity的,要么是Application的。它的特点是,生命周期只局限于某个页面的生命周期,亦或是App的生命周期。当绑定广播的Activity或App被销毁后,就无法再接收广播。同样地,订阅这样一个广播,我们需要分3步走:
- 创建继承自BroadcastReceiver的类,代码如下:
class MyBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// 当接收到广播时,这个方法会被调用,intent参数,包含了广播所附带的数据。
}
}
- 创建BroadcastReceiver子类的实例,代码如下:
val br: BroadcastReceiver = MyBroadcastReceiver()
- 创建IntentFilter,添加订阅事件,并注册广播。
val filter = IntentFilter()
// 添加要订阅的广播事件
filter.addAction(Intent.ACTION_SCREEN_OFF)
// 注册广播
registerReceiver(br, filter)
三,取消注册广播。
因为动态广播受限于Activity或Application的生命周期,简而言之,动态广播应该在合适的时机被取消注册。让我们先看看,如何取消注册一个广播:
// val br: BroadcastReceiver = MyBroadcastReceiver()
unregisterReceiver(br)
可以看到,取消注册广播很简单,一行代码就搞定。那么,为什么,需要在合适的时机去取消注册广播?因为,动态广播(非本地广播)会持有Activity的上下文context。在Activity被销毁时,如果没有取消所有注册的广播,就会造成内存泄露。让我们看看源码部分,关于注册和反注册广播接收者的部分代码。在LoadedApk类里面,有一个成员变量,如下所示:
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>>
mReceivers = new ArrayMap<>();
可以看到,每一个Context,可以绑定多个广播接收者(BroadcastReciver)。让我们再看看,取消注册广播的源码实现部分。可以看到,只有当map.size为0时,即所有广播接收者都取消注册后,这个Context才会被释放。
// 省略....
synchronized (mReceivers) {
ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = mReceivers.get(context);
LoadedApk.ReceiverDispatcher rd = null;
if (map != null) {
rd = map.get(r);
if (rd != null) {
map.remove(r);
if (map.size() == 0) {
mReceivers.remove(context);
}
// 省略其它代码
}
}
}
// 省略....
四,发送广播消息【未完待续】。
根据广播消息的接收次序,我们可以把广播消息,分为有序广播和无序广播。
- 有序广播
有序广播是指,当一个广播消息被发送出去时,多广播接收者,会依次接收到这个消息,同一时间内,只有一个广播接收者在处理这条广播消息,任意一个正在处理广播消息的广播接收者,都可以中断这个传播链。其原理,类似于责任链模式。我们,可以通过,配置这个属性android:priority
,来提高广播接收者的优先级,优先级越高的,越先接收到消息。这个属于的值,必须是整数,而且,数值越大,优先级越高。
五,广播对保活的影响。
当广播接收者,接收到广播消息时,onReceive()
方法会被调用。在onReceive()
方法执行期间,当前应用会被认为是前台应用。但当这个方法执行完毕后,当前应用的优先级,又会降回原来app所处的保活等级。因此,可以通过在onReceive()
里面,创建一个守护线程,来确保onReceive()
一直在执行,就可以使得app所处的保活等级较高,不会轻易被系统回收。【注:这也无法根本性的解决保活问题,只能说是最大化保活的手段之一。可以在面试,被问到如何保活时,提出来讲讲】
参考资料: