11、BroadcastReceiver 管理:AMS对动态/静态广播的注册、分发、有序广播流程
广播,说白了就是 Android 系统里的一种「喊话」机制。一个组件喊一嗓子,其他感兴趣的组件就能听到。但这里面的门道,比你想象的要深得多。
我刚开始接触广播时,觉得这玩意儿挺简单——注册一下,收到消息,完事。直到有一次线上事故,静态广播没收到,排查了整整两天……嗯,从那以后,我再也不敢小看 BroadcastReceiver 的管理了。
今天我们就来聊聊,AMS 到底是怎么管理这些广播的。从注册到分发,再到有序广播的流转,我把这些年踩过的坑一并告诉你。
11.1 广播的两种注册方式
广播接收器有两种注册方式:动态注册和静态注册。这两种方式,AMS 的处理逻辑完全不同。
11.1.1 动态注册
动态注册是在代码里调用 registerReceiver() 完成的。说白了,就是你的应用主动告诉 AMS:「嘿,我对某个广播感兴趣,有消息了通知我。」
AMS 收到注册请求后,会做以下几件事:
- 解析 IntentFilter,提取 action、category、data 等信息
- 创建一个 ReceiverDispatcher 对象,用于管理这个接收器
- 将接收器信息存入 AMS 的
mRegisteredReceivers列表中 - 同时建立 action 到接收器的映射关系,方便快速查找
这里有个关键点:动态注册的接收器,生命周期和注册它的组件绑定。组件销毁了,接收器也就自动注销了。我曾经见过有人忘记在 onDestroy() 里调用 unregisterReceiver(),结果导致内存泄漏,AMS 里残留了一堆无效的接收器。
11.1.2 静态注册
静态注册是在 AndroidManifest.xml 里声明的。这种方式不需要代码参与,应用安装时,PackageManagerService(PMS)就会解析清单文件,把接收器信息存起来。
AMS 在处理静态广播时,会去 PMS 查询所有匹配的接收器。这个过程涉及到跨进程通信,所以性能上比动态注册要慢一些。
我个人习惯是:能动态注册的就别用静态注册。为什么呢?因为 Android 8.0 之后,静态注册的隐式广播大部分都被限制了。除非你声明了特定的权限,或者发送的是显式广播,否则静态接收器根本收不到消息。
- 动态注册:由 AMS 的 mRegisteredReceivers 管理,生命周期跟随组件
- 静态注册:由 PMS 解析并存储,AMS 通过 PMS 查询获取
11.2 广播的分发流程
广播的分发,是 AMS 最核心的工作之一。我们来看看,当你调用 sendBroadcast() 时,系统内部到底发生了什么。
11.2.1 发送广播的入口
应用层调用 sendBroadcast() 后,会通过 Binder 调用到 AMS 的 broadcastIntent() 方法。AMS 会做以下检查:
- 权限检查:发送者是否有权限发送该广播
- 广播类型检查:是普通广播、有序广播还是粘性广播
- 接收器匹配:找出所有符合条件的接收器
我记得有一次,我发送了一个自定义广播,结果死活收不到。排查了半天,发现是权限声明的问题。AMS 在分发广播前,会检查发送者和接收者的权限,任何一个不匹配,广播就会被丢弃。
11.2.2 接收器的匹配过程
AMS 匹配接收器时,会分两步走:
- 动态接收器匹配:遍历 mRegisteredReceivers,检查 IntentFilter 是否匹配
- 静态接收器匹配:通过 PMS 查询所有静态注册的接收器,再做一次过滤
匹配完成后,AMS 会把所有符合条件的接收器封装成一个 BroadcastRecord 对象。这个对象里包含了广播的所有信息,以及待分发的接收器列表。
11.3 有序广播的完整流程
有序广播和普通广播最大的区别在于:接收器是按顺序执行的,而且可以中断广播的传播。
你想想看,普通广播就像在群里发消息,所有人都能同时看到。而有序广播就像开会时一个个发言,每个人说完才能轮到下一个,而且主持人可以随时叫停。
11.3.1 有序广播的注册
有序广播的接收器注册方式和普通广播一样,区别在于发送时调用的是 sendOrderedBroadcast()。AMS 在处理有序广播时,会为每个接收器设置优先级,优先级高的先执行。
优先级在动态注册时通过 IntentFilter 的 setPriority() 设置,静态注册时通过 android:priority 属性设置。数值越大,优先级越高。
11.3.2 分发过程
AMS 分发有序广播时,会按以下步骤执行:
- 从 BroadcastRecord 中取出优先级最高的接收器
- 通过 Binder 调用接收器的
onReceive()方法 - 等待接收器执行完毕(同步执行)
- 检查接收器是否调用了
abortBroadcast() - 如果调用了,停止分发;否则继续下一个接收器
这里有个坑:有序广播的接收器是在主线程中执行的。如果你的 onReceive() 方法耗时太长,会阻塞整个广播的分发流程。我曾经在项目中遇到过,一个接收器做了网络请求,导致后续所有接收器都超时了。
onReceive() 方法最多只能执行 10 秒。超过这个时间,AMS 会认为接收器超时,直接跳过它继续分发。如果你的接收器需要做耗时操作,请使用 goAsync() 或启动 Service。
11.3.3 结果传递
有序广播还有一个特性:接收器可以修改广播的结果数据。通过 setResultData()、setResultExtras() 等方法,每个接收器都能修改结果,下一个接收器拿到的是修改后的数据。
这个特性在某些场景下非常有用。比如,你可以让第一个接收器做一些预处理,第二个接收器再基于处理后的数据做业务逻辑。
11.4 核心流程图
下面这张图,是我根据 AMS 源码梳理出来的广播分发流程。你看完应该能对整个链路有个清晰的认识。
11.5 避坑指南
做广播开发这么多年,我总结了几条经验,分享给你:
- 不要在主线程做耗时操作:广播的
onReceive()默认在主线程执行,超过 10 秒会被 AMS 干掉 - 动态注册记得注销:在
onDestroy()或onPause()中调用unregisterReceiver(),避免内存泄漏 - 有序广播慎用 abort:调用
abortBroadcast()会中断后续所有接收器,确保你真的需要这么做 - 静态广播注意版本限制:Android 8.0 之后,大部分隐式静态广播都被限制了,改用动态注册或显式广播
好了,关于 BroadcastReceiver 的管理,我们就聊到这里。记住,广播的核心在于 AMS 如何管理接收器的生命周期和分发顺序。理解了这些,你就能在项目中游刃有余地使用广播了。
公众号:蓝海资料掘金营,微信deep3321