12. BroadcastReceiver 广播接收器:广播机制原理、静态注册与动态注册、标准广播与有序广播、本地广播、Android 8+ 广播限制
广播接收器,说白了就是 Android 系统里的一种「消息通知机制」。我刚开始学 Android 时,觉得它就像个传声筒——某个地方喊一嗓子,所有感兴趣的人都能听到。但实际用起来,坑还真不少。
今天咱们就把广播机制彻底捋一遍。从原理到实战,从老版本到新版本的限制,我都会结合自己的踩坑经历来讲。
12.1 广播机制原理
Android 里的广播,本质上是一种 观察者模式 的实现。系统或应用发送一个广播 Intent,所有注册了对应 IntentFilter 的 BroadcastReceiver 都会收到通知。
我习惯把广播理解成「喇叭」:
- 发送者:系统或应用,通过 sendBroadcast() 等方法喊话
- 接收者:BroadcastReceiver,负责监听并处理消息
- Intent:就是那条消息的内容,包含 Action、Category、Data 等
广播的生命周期很短。Receiver 的 onReceive() 方法执行完后,系统就会认为它结束了。所以 不要在 onReceive() 里做耗时操作,比如网络请求或数据库写入。我曾经有个同事在 onReceive() 里直接开了个线程去上传日志,结果线程还没跑完,Receiver 就被系统回收了,数据丢了不说,还查了半天 bug。
核心要点:onReceive() 运行在主线程,最多只能执行 10 秒。超时会被系统判定为 ANR。
12.2 静态注册与动态注册
广播注册有两种方式。我个人的习惯是:能用动态就别用静态。为什么?看完你就明白了。
12.2.1 静态注册
在 AndroidManifest.xml 里声明 Receiver,并指定要监听的 Action。应用安装后,即使没启动,也能收到广播。
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
静态注册的优点很明显:应用被杀掉也能收到广播。比如监听开机完成、网络变化等系统事件。
但缺点也很要命:
- Android 8.0 之后,大部分隐式广播都不支持静态注册了
- Receiver 会一直存在,浪费资源
- 调试起来比较麻烦,得装包、重启才能测试
注意:Android 8.0+ 对静态注册的限制非常严格。除了少数白名单广播(如 BOOT_COMPLETED、TIMEZONE_CHANGED 等),其他隐式广播都无法通过静态注册接收。如果你还在用静态注册监听网络变化,嗯,代码可能已经失效了。
12.2.2 动态注册
在代码里通过 registerReceiver() 注册,在不需要时通过 unregisterReceiver() 注销。我建议在 Activity 或 Fragment 的 onStart() 里注册,在 onStop() 里注销。
class MainActivity : AppCompatActivity() {
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// 处理广播
val action = intent.action
Toast.makeText(context, "收到广播: $action", Toast.LENGTH_SHORT).show()
}
}
override fun onStart() {
super.onStart()
val filter = IntentFilter().apply {
addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED)
}
registerReceiver(receiver, filter)
}
override fun onStop() {
super.onStop()
unregisterReceiver(receiver)
}
}
动态注册的好处:
- 生命周期可控,不会浪费资源
- 不受 Android 8.0 限制,所有广播都能监听
- 调试方便,改完代码直接跑
但有个坑:一定要成对出现。注册了不注销,会导致内存泄漏。我曾经在项目里见过一个 Fragment,每次 onResume() 都注册一次,但 onPause() 里忘了注销。结果切几次页面,Receiver 被注册了十几次,每次广播来都触发十几个回调。嗯,那场面相当壮观。
我的建议:能用动态注册就用动态注册。只有像开机广播这种必须在应用启动前收到的场景,才考虑静态注册。
12.3 标准广播与有序广播
广播按发送方式分为两种:标准广播和有序广播。你想想看,它们有什么区别?
12.3.1 标准广播
通过 sendBroadcast() 发送。所有 Receiver 几乎同时收到广播,没有顺序,也不能截断。
val intent = Intent("com.example.MY_CUSTOM_BROADCAST")
sendBroadcast(intent)
标准广播效率高,适合「通知」类的场景。比如告诉所有页面「用户登录了」,大家各自更新 UI。
12.3.2 有序广播
通过 sendOrderedBroadcast() 发送。Receiver 按优先级顺序接收,可以截断广播,不让后面的 Receiver 收到。
val intent = Intent("com.example.MY_ORDERED_BROADCAST")
sendOrderedBroadcast(intent, null)
在 AndroidManifest 里设置优先级:
<receiver android:name=".HighPriorityReceiver">
<intent-filter android:priority="100">
<action android:name="com.example.MY_ORDERED_BROADCAST" />
</intent-filter>
</receiver>
优先级越高,越先收到。在 onReceive() 里可以调用 abortBroadcast() 来截断广播。
我在项目中用过有序广播来处理「消息推送优先级」:高优先级的 Receiver 先处理紧急消息,如果处理完了,就截断广播,低优先级的 Receiver 就不用再处理了。这样能避免重复操作。
对比总结:
| 类型 | 发送方式 | 接收顺序 | 能否截断 | 典型场景 |
|---|---|---|---|---|
| 标准广播 | sendBroadcast() | 无序 | 不能 | 通知刷新、状态同步 |
| 有序广播 | sendOrderedBroadcast() | 按优先级 | 能 | 优先级处理、消息拦截 |
12.4 本地广播 (LocalBroadcastManager)
本地广播,说白了就是 应用内广播。它只在当前 App 内部传递,不会跨进程,安全性更高,效率也更好。
我记得在早期项目里,大家习惯用全局广播来做组件间通信。结果有一次,某个第三方 SDK 也监听了同样的 Action,导致我们的内部消息被泄露了出去。从那以后,我只要做应用内通信,一律用 LocalBroadcastManager。
// 发送本地广播
val intent = Intent("com.example.LOCAL_BROADCAST")
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
// 注册本地广播
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// 处理广播
}
}
LocalBroadcastManager.getInstance(this).registerReceiver(receiver, IntentFilter("com.example.LOCAL_BROADCAST"))
// 注销
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver)
本地广播的优点:
- 安全:其他应用收不到,不用担心消息泄露
- 高效:不需要跨进程通信,性能更好
- 轻量:不需要在 Manifest 里声明
注意:LocalBroadcastManager 在 Android 12 上已被标记为废弃。Google 推荐使用 LiveData、Flow 或 SharedFlow 替代。不过,如果你还在维护老项目,LocalBroadcastManager 依然能用,只是不建议在新项目里用了。
12.5 Android 8+ 广播限制
Android 8.0(API 26)开始,系统对广播做了严格限制。我当年升级项目时,被这个坑得不轻。很多静态注册的广播突然不工作了,排查了半天才发现是系统限制。
主要限制有两点:
- 隐式广播限制:大部分隐式广播(没有指定包名的广播)无法通过静态注册接收。只有少数白名单广播例外。
- 后台执行限制:应用在后台时,发送的广播会被延迟或丢弃。
白名单广播列表(部分):
| Action | 说明 |
|---|---|
| ACTION_BOOT_COMPLETED | 开机完成 |
| ACTION_TIMEZONE_CHANGED | 时区变化 |
| ACTION_TIME_CHANGED | 时间变化 |
| ACTION_BATTERY_LOW | 电量低 |
| ACTION_POWER_CONNECTED | 连接电源 |
| ACTION_POWER_DISCONNECTED | 断开电源 |
那怎么办?我的经验是:
- 能用动态注册的,全用动态注册
- 必须用静态注册的,检查是否在白名单里
- 如果不在白名单,考虑用 JobScheduler 或 WorkManager 替代
避坑指南:我曾经在项目里用静态注册监听网络变化,结果在 Android 8.0 手机上完全没反应。后来改成动态注册,在 Application 的 onCreate() 里注册,问题就解决了。记住:动态注册不受限制。
12.6 知识体系图
下面这张图总结了广播机制的核心知识点,方便你快速回顾:
广播机制看似简单,但涉及的知识点不少。从注册方式到广播类型,再到版本兼容,每一步都有坑。我的建议是:先理解原理,再动手实践。遇到问题多看看官方文档,别自己瞎猜。
好了,广播接收器就讲到这里。记住我今天说的几个关键点,尤其是 Android 8.0 的限制和本地广播的使用场景。这些经验都是我用加班换来的,希望能帮你少走弯路。