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)开始,系统对广播做了严格限制。我当年升级项目时,被这个坑得不轻。很多静态注册的广播突然不工作了,排查了半天才发现是系统限制。

主要限制有两点:

  1. 隐式广播限制:大部分隐式广播(没有指定包名的广播)无法通过静态注册接收。只有少数白名单广播例外。
  2. 后台执行限制:应用在后台时,发送的广播会被延迟或丢弃。

白名单广播列表(部分):

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 知识体系图

下面这张图总结了广播机制的核心知识点,方便你快速回顾:

BroadcastReceiver 广播机制知识体系 BroadcastReceiver 注册方式 静态注册 动态注册 广播类型 标准广播 有序广播 本地广播 Android 8+ 广播限制 隐式广播限制 后台执行限制 核心原则:能用动态不用静态,应用内通信用本地广播

广播机制看似简单,但涉及的知识点不少。从注册方式到广播类型,再到版本兼容,每一步都有坑。我的建议是:先理解原理,再动手实践。遇到问题多看看官方文档,别自己瞎猜。

好了,广播接收器就讲到这里。记住我今天说的几个关键点,尤其是 Android 8.0 的限制和本地广播的使用场景。这些经验都是我用加班换来的,希望能帮你少走弯路。


公众号:蓝海资料掘金营,微信 deep3321