BroadcastReceiver
本教程共 100 篇 · 第 35 篇 · 更新于 2026-07-28 · 约 8 分钟阅读
35. BroadcastReceiver
本节目标:搞懂广播接收器是什么、怎么收系统广播、怎么发自定义广播,学完能在应用里监听飞行模式切换、电量变化等系统事件。
广播是什么
打个比方:广播就像小区里的广播喇叭。物业(系统)喊一声「停水通知」,所有装了对应喇叭(接收器)的住户都能听到。住户听到后各自决定干什么——有的去接水,有的只是记一下。
Android 里的广播(Broadcast)就是这种「一对多」的消息机制,遵循发布-订阅模式。系统在发生各种事件时会发广播,比如设备开机完成、飞行模式切换、电量不足。应用也可以发自己的广播,通知别的应用「我这有新数据了」。
广播接收器(BroadcastReceiver)就是那个「喇叭」,负责接收并处理这些消息。
两种注册方式
接收广播有两种方式,区别在于「喇叭装在哪」:
- 静态注册(清单声明):在
AndroidManifest.xml里用<receiver>标签声明。应用装上就生效,哪怕应用没运行,广播来了系统也能把应用拉起来。 - 动态注册(上下文注册):在代码里调用
registerReceiver()注册。只在注册期间有效,注册它的上下文(比如 Activity)销毁了就收不到了。
Warning从 Android 8.0(API 26)开始,系统对静态注册做了限制:大多数隐式广播不能再通过清单声明接收。只有少数豁免广播(如
BOOT_COMPLETED)还能静态注册。所以现在主流做法是动态注册。
静态注册长什么样
先看清单里的声明:
<receiver
android:name=".BootCompletedReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
android:exported 设为 false 表示只接收应用自己发的广播。如果要收系统或其他应用的广播,设成 true。
接收器类继承 BroadcastReceiver,实现 onReceive():
class BootCompletedReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
// 开机完成后想做的事,比如启动一个定时任务
Log.d("BootReceiver", "设备开机完成")
}
}
}
Note开机广播需要
RECEIVE_BOOT_COMPLETED权限,别忘了在清单里加上<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />。
动态注册实战:监听飞行模式
动态注册是日常用得最多的方式。下面监听飞行模式切换,用 Kotlin 写一个完整例子。
先创建接收器:
class AirplaneModeReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// 飞行模式广播会带一个 "state" 布尔 extra
val isAirplaneModeOn = intent.getBooleanExtra("state", false)
if (isAirplaneModeOn) {
Toast.makeText(context, "飞行模式已开启", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "飞行模式已关闭", Toast.LENGTH_SHORT).show()
}
}
}
在 Activity 里注册和注销:
class MainActivity : ComponentActivity() {
private val receiver = AirplaneModeReceiver()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { /* Compose 内容 */ }
}
override fun onStart() {
super.onStart()
val filter = IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)
// Android 13+ 必须指定导出标志
ContextCompat.registerReceiver(
this,
receiver,
filter,
ContextCompat.RECEIVER_NOT_EXPORTED
)
}
override fun onStop() {
super.onStop()
unregisterReceiver(receiver)
}
}
Tip从 Android 13(API 33)开始,动态注册必须指定
RECEIVER_EXPORTED或RECEIVER_NOT_EXPORTED标志,否则会崩溃。用ContextCompat.registerReceiver()可以兼容旧版本。只收自己应用发的广播用RECEIVER_NOT_EXPORTED,要收系统或其他应用的广播用RECEIVER_EXPORTED。
注册时机的选择
动态注册要选对生命周期范围,原则是「只在真正需要时注册,不需要了立刻注销」。常见选择:
onStart()/onStop():界面可见时才接收。onResume()/onPause():界面可交互时才接收。onCreate()/onDestroy():整个 Activity 存活期间都接收。
Warning一定要在对应的回调里注销接收器!忘了注销会导致内存泄漏——接收器持有 Activity 的引用,Activity 销毁了却回收不掉。在
onDestroy()里注销,别在onSaveInstanceState()里,因为那个方法不一定被调用。
发送自定义广播
应用也能自己发广播。比如下载完数据后通知其他模块刷新。
val intent = Intent("com.example.app.DATA_UPDATED").apply {
putExtra("extra_data", "新内容")
// 限定只发给同一包名的应用,更安全
setPackage("com.example.app")
}
context.sendBroadcast(intent)
Tip发广播时用
setPackage()限定包名是个好习惯。不加限制的话,任何应用都能收到你的广播,敏感信息可能泄露。
onReceive 的注意事项
onReceive() 运行在主线程,而且系统希望它快点执行完。一旦方法返回,系统可能随时回收进程。
所以有几条规矩:
- 别在
onReceive()里做耗时操作(网络请求、大文件读写)。 - 需要耗时的话,调用
goAsync()把PendingResult传给后台线程,但也要在 10 秒内搞定。 - 别直接启动 Activity,推荐用通知代替。
override fun onReceive(context: Context, intent: Intent) {
// goAsync 让广播在后台线程处理完才结束
val pendingResult = goAsync()
CoroutineScope(Dispatchers.IO).launch {
try {
// 耗时操作,比如存数据
doSomeWork()
} finally {
pendingResult.finish() // 必须调用,否则系统会认为广播没处理完
}
}
}
常用系统广播
| 广播 Action | 触发时机 |
|---|---|
ACTION_BOOT_COMPLETED | 系统启动完成 |
ACTION_AIRPLANE_MODE_CHANGED | 飞行模式开关切换 |
ACTION_BATTERY_LOW | 电量不足 |
ACTION_TIME_TICK | 每分钟一次(只能动态注册) |
CONNECTIVITY_ACTION | 网络连接变化(Android 7.0 起只能动态注册) |
Note随着版本演进,系统广播的行为一直在收紧。Android 16 中,跨进程的广播优先级不再保证顺序,只有同一进程内的优先级才生效。写代码时别依赖广播的接收顺序。
小结
广播接收器是 Android 四大组件之一,本质是一套发布-订阅消息机制。静态注册适合开机自启这类少数场景,动态注册是现在的主流。记住三件事:动态注册要配对注销、onReceive 要快、发广播要限定包名。