Android Broadcast Receiver完全入门指南

1. 什么是 BroadcastReceiver?

BroadcastReceiver(广播接收器)是 Android 四大组件之一,它就像是一个"订阅者":系统或应用会发出各种各样的"广播消息",而你可以编写一个 BroadcastReceiver 来监听并响应你感兴趣的消息,比如:

  • 设备电量低了

  • 网络连接状态发生了变化

  • 用户拔掉了耳机

  • 应用安装或卸载

  • 闹钟时间到了

简单说,BroadcastReceiver 让你能监听系统全局事件或自定义事件,并做出响应

2. BroadcastReceiver 的作用与应用场景

2.1 监听系统事件

无需实时运行,只在事件发生时被系统唤醒并执行一段代码,省电又高效。

2.2 应用内/跨应用通信

你可以在自己 App 内部发送广播让不同组件收到消息,也可以让其他 App 收到你发的广播(或监听其他 App 的广播),实现解耦。

2.3 开启其他组件

收到广播后,可以启动一个 ActivityService 或发通知,但注意后台执行限制。

2.4 典型场景

  • 电量变化:电量低于 20% 时提示用户或自动进入省电模式。

  • 网络变化:Wi-Fi 断开时自动暂停下载。

  • 耳机插拔:音乐 App 检测到耳机拔出时自动暂停播放。

  • 应用安装/卸载:清理与该应用相关的缓存文件。

  • 闹钟/定时任务 :结合 AlarmManager 执行定时任务(但有更优方案)。

  • 应用内通信:购物车更新,通知其他页面刷新。

3. 广播的类型

从传播方式看,广播分为两类:

类型 特点 典型使用
标准广播(Normal Broadcasts) 完全异步,所有接收器几乎同时收到,无法被拦截,不可被修改。 系统通知:时区变更、屏幕开启等。
有序广播(Ordered Broadcasts) 一次发送给一个接收器,按优先级顺序传递,前面的接收器可以修改结果甚至中止广播。 需要处理顺序的场景,如短信拦截。

从广播来源看,分为系统广播自定义广播 。系统广播由 Android 系统发送,如 ACTION_BOOT_COMPLETED(开机完成);自定义广播则是开发者自己发送的。

4. 如何接收广播:注册 BroadcastReceiver

想让 BroadcastReceiver 监听广播,必须进行注册 。注册方式有两种:静态注册 (清单文件)和动态注册(代码)。

4.1 静态注册(清单注册)

AndroidManifest.xml 中声明,即使应用未启动,也能接收广播(受版本限制,见 7.3 节)。系统广播中的开机广播、应用安装等必须使用静态注册才能收到。

步骤

  1. 创建一个类,继承 BroadcastReceiver,实现 onReceive()

  2. <application> 节点下注册 <receiver>,并添加 <intent-filter> 指定要监听的广播动作。

示例:监听开机完成广播

kotlin

复制代码
// MyBootReceiver.kt
class MyBootReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
            // 开机完成,可以做初始化操作
        }
    }
}

AndroidManifest.xml 中:

xml

复制代码
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<application...>
    <receiver
        android:name=".MyBootReceiver"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
</application>

注意:从 Android 8.0 (API 26) 开始,大部分系统广播无法再通过静态注册接收(除了少数白名单广播,如开机,见 7.3)。

4.2 动态注册(代码注册)

ActivityService 等组件中通过代码注册,通常与生命周期绑定,应用退出后自动失效(因为组件销毁)。

步骤

  1. 实例化自定义的 BroadcastReceiver

  2. 创建 IntentFilter,添加要监听的动作。

  3. 调用 registerReceiver(receiver, filter) 进行注册。

  4. 别忘了在合适时机取消注册 (如 onPause/onStop 中调用 unregisterReceiver()),避免内存泄漏或异常。

示例:监听网络变化

kotlin

复制代码
class MainActivity : AppCompatActivity() {
    private val networkReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            // 网络状态变化,更新 UI
        }
    }

    override fun onStart() {
        super.onStart()
        val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        registerReceiver(networkReceiver, filter)
    }

    override fun onStop() {
        super.onStop()
        unregisterReceiver(networkReceiver)
    }
}

注意CONNECTIVITY_ACTION 在 Android 7.0 (API 24) 及更高版本同样受到后台限制,建议使用 ConnectivityManager.NetworkCallback 代替广播监听网络变化。这里只是为了演示动态注册的步骤。

4.3 两种注册方式对比

特性 静态注册 动态注册
何时接收 应用未启动时也能收到(限制广播) 只在组件存在时才能接收
生命周期 独立于组件,有广播就调用 随注册组件(Activity等)生命周期
系统开销 相对重,常驻内存 轻量,随组件回收
适用场景 需要开机自启等必需场景 大部分应用内实时监听场景
取消注册 无需手动取消 必须手动取消(通常在 onStop/onDestroy)

5. 发送广播

你可以发送系统定义的广播,但通常开发者发送的是自定义广播。

5.1 发送标准广播

kotlin

复制代码
val intent = Intent("com.example.MY_CUSTOM_ACTION")
intent.putExtra("key", "value")
// 发送给所有匹配的接收器(包括其他App)
sendBroadcast(intent)
// 如果需要限制接收方,可以添加权限
// sendBroadcast(intent, "com.example.MY_PERMISSION")

5.2 发送有序广播

kotlin

复制代码
val intent = Intent("com.example.MY_ORDERED_ACTION")
// 最后一个参数为最终接收器,当所有接收器处理完后如果没有被中止,会收到结果
sendOrderedBroadcast(intent, null, object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        // 最终执行,可以获取到前面接收器设置的结果
        val resultData = resultData
    }
}, null, Activity.RESULT_OK, null, null)

在有序广播的接收器中,可以:

  • setResultData(data) 修改数据

  • abortBroadcast() 中止传递(需要权限,且仅对有序广播有效)

5.3 应用内发送(LocalBroadcastManager,已弃用)

为了安全和性能,过去常使用 LocalBroadcastManager 限制广播仅在进程内传递。但从 AndroidX 开始,此组件已被弃用 ,官方建议用 LiveDataFlowEventBus 替代。如果你仍需要应用内广播,可以使用带有自定义权限的广播,或直接使用观察者模式。

6. BroadcastReceiver 的生命周期

BroadcastReceiver 的生命周期非常短暂,只在 onReceive() 方法执行期间有效。

  • onReceive(context, intent) :当广播被接收时调用。该方法在主线程 执行,必须在 10 秒内完成,否则会导致 ANR。

  • BroadcastReceiver 对象在 onReceive() 返回后会变成无效状态(不再活跃),系统随时可能回收。

绝对不要在 onReceive() 中做耗时操作 (如网络请求、复杂计算、大文件读写)。如果必须做耗时操作,正确的做法是启动一个 Service(某些情况下受限制)或调度 WorkManager 任务。

7. 权限与安全

7.1 发送广播时添加权限

防止未授权的应用接收你的广播:

kotlin

复制代码
sendBroadcast(intent, "com.example.MY_PERMISSION")

只有声明了 MY_PERMISSION 权限的接收器才能收到该广播。

7.2 接收广播时要求发送方持有权限

在注册接收器时,你可以在 registerReceiver() 中指定一个 broadcastPermission,只有持有该权限的发送方才能向你的接收器发送广播。同样,静态注册时可使用 android:permission 属性。

7.3 Android 版本注意事项(必读)

  • Android 8.0 (API 26) :除了少数隐式广播(如开机广播),静态注册的隐式广播接收器将不再工作 。大部分系统广播必须动态注册。发送隐式广播(即目标是 action 而非组件类名)也受到限制,建议使用显式广播(指定 ComponentName)。

  • Android 14 (API 34):进一步收紧,隐式广播发送者可能需要申报权限,且缓存广播更加严格。

  • 为了兼容性 :对于需要一直运行的功能,考虑使用 WorkManagerJobScheduler 代替广播接收器。

白名单广播 (可在清单中静态注册而无需应用运行)包括:ACTION_BOOT_COMPLETEDACTION_LOCALE_CHANGED 等,具体参考官方文档。

8. 实战演练:一个电量变化监听小案例

假设我们要做一个在电量低于 15% 时提醒用户的功能。因为 ACTION_BATTERY_LOW 是粘性广播且不允许静态注册,我们用动态注册监听 ACTION_BATTERY_CHANGED(但该广播只能动态注册,且系统会频繁发送)。

代码清单(在 Activity 中)

kotlin

复制代码
class BatteryActivity : AppCompatActivity() {
    private lateinit var batteryReceiver: BroadcastReceiver

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_battery)

        batteryReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                val level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
                val scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
                val batteryPct = level * 100 / scale.toFloat()
                if (batteryPct < 15) {
                    // 低电量,发通知或显示对话框
                    showLowBatteryWarning()
                }
            }
        }
    }

    override fun onStart() {
        super.onStart()
        val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
        registerReceiver(batteryReceiver, filter)
    }

    override fun onStop() {
        super.onStop()
        unregisterReceiver(batteryReceiver)
    }

    private fun showLowBatteryWarning() {
        // 这里可以发通知
    }
}

9. 静态注册自定义广播的完整例子(仅应用内)

如果你想在应用内用静态注册接收自定义广播(例如通知其他组件),可以使用显式广播以保证安全且不受系统限制。

发送方:

kotlin

复制代码
val intent = Intent(this, MyInternalReceiver::class.java)
intent.action = "com.example.INTERNAL_ACTION"
sendBroadcast(intent)

接收方(清单中注册,exported=false 确保安全):

xml

复制代码
<receiver
    android:name=".MyInternalReceiver"
    android:exported="false">
    <intent-filter>
        <action android:name="com.example.INTERNAL_ACTION" />
    </intent-filter>
</receiver>

kotlin

复制代码
class MyInternalReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        // 响应内部事件
    }
}

10. 常见问题与最佳实践

10.1 别忘了取消动态注册

动态注册必须在合适的生命周期方法中取消,通常 onPauseonStop 中执行 unregisterReceiver()。否则当你退出 Activity 后接收器仍然存在,系统会抛出 leaked 异常,严重时导致崩溃。

10.2 不要做耗时操作

onReceive 运行在主线程,应迅速返回。耗时任务应:

  • 启动 Service(注意后台限制)

  • 使用 goAsync()(需要 API 11+,但较少使用)

  • 调度 WorkManager

10.3 尽量减少静态注册,多用动态 + 生命周期感知组件

Android 8.0+ 限制后,静态注册的用途已经很有限。除非你确实需要应用不在运行时接收广播,否则建议动态注册,并结合 LiveDataLifecycle 组件自动管理注册/取消。

10.4 使用 LocalBroadcastManager 的替代品

LocalBroadcastManager 被弃用,建议使用以下替代:

  • LiveData / SharedFlow:组件间通信。

  • EventBus(GreenRobot):轻量级事件总线。

  • Kotlin Flow:应用内数据流。

  • 如果必须使用广播,且仅限应用内,可以使用带权限的显式广播。

10.5 Android 13 (API 33) 引入的通知权限

如果你的广播接收器会弹出通知,需要确保已获得通知权限(POST_NOTIFICATIONS)。对于静态注册的接收器启动前台服务,也会受到新限制,详细查阅对应版本的行为变更。

11. 总结

  • BroadcastReceiver 是 Android 中一个轻量级的消息订阅组件,用于接收系统或应用内广播消息。

  • 注册方式分为静态注册 (清单)和动态注册(代码),目前大部分场景使用动态注册。

  • 广播分为标准广播有序广播,后者可以按优先级处理和中止。

  • 注意版本适配,尤其是 Android 8.0 以后对静态注册的隐式广播限制,以及 Android 14 的进一步收紧。

  • 始终在合适的生命周期取消动态注册,避免泄漏;onReceive() 中不做耗时操作。

  • 谨慎使用广播的应用内通信,可逐步用 LiveData/Flow 替换,但广播仍有用武之地。


延伸阅读推荐:

相关推荐
曹牧22 分钟前
文档格式:OFD
java
豆角焖肉1 小时前
Maven进阶与搭建私服
java·maven
空中湖1 小时前
Spring AI Agent 编排:ReAct 模式 + 多 Agent 协作实战
人工智能·spring·react.js
大不点wow1 小时前
Java序列化与反序列化:让对象走出JVM
java·开发语言·jvm
止语Lab1 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活1 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
噢,我明白了1 小时前
Java中日期和字符串的处理
java·开发语言·日期
程序员天天困1 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn1 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
dkbnull1 小时前
Spring Boot请求处理组件对比详解
java·spring boot