broadcast intent
broadcast intent:保留第四版正文机制,以所有者—状态—结果合同、事件轨迹和章专属故障完成可重放验收。
学习目标
- 能沿“broadcast intent”的用户事件解释Android组件、状态所有者、线程与销毁边界。
- 能围绕“理解 Android 的广播机制:系统广播、自定义广播、动态注册与静态注册的区别,以及 Android 8.0+ 对隐式广播的限制和现代替代方案。”改出一个可运行结果,并用前后状态而非组件数量验收。
- 能在旋转、进程重建、拒权、离线或无效输入中选择适用反例,定位首个状态分叉。
- 能用Intent合同、解析目标、权限、返回结果和重复副作用独立重放结论,并标明第四版机制与现代targetSdk政策的边界。
为什么需要"广播"?
你手机里装的 App 互不认识,但它们需要在一些事情发生时知道该怎么做。比如:系统连上了 Wi-Fi——下载管理器要开始下载离线地图、音乐 App 可能要切换音质、你的 App 要触发一次数据同步。如果每个 App 都自己去轮询"Wi-Fi 连上了吗?",整个系统会被轮询搞垮。
Android 的广播机制就是为此设计的:系统是唯一的"大喇叭",发生什么事它喊一声,谁需要听就自己来注册。喊的人不管有多少听众,听众也不关心还有谁在听。这种发布-订阅的松耦合模式,就是 Android 广播的核心思想。
这章要解决的核心问题:怎么让你的 App 感知到手机系统里正在发生的事(Wi-Fi 变化、电量低、开屏关屏),以及怎么用广播让 App 内部各组件解耦通信。
广播接收器的基本使用
什么是 BroadcastReceiver
↡BroadcastReceiver 是 Android 四大组件之一,专门用来接收系统或其他 App 发出的广播消息。它像一个「订阅器」——你告诉系统你关心哪些事件,事件发生时系统会调用你的 onReceive() 方法。它是 Android 发布-订阅模式的核心实现。是 Android 四大组件中最轻量的一个。它的作用非常简单——定义对某个事件感兴趣的"耳朵"。
一个 BroadcastReceiver 只需要实现一个方法:
class MyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// 收到广播时执行这里的代码
when (intent.action) {
Intent.ACTION_POWER_CONNECTED -> {
// 充电器插上了
}
Intent.ACTION_BATTERY_LOW -> {
// 电量低了
}
}
}
}两种注册方式:动态 vs 静态
| 动态注册(代码中) | 静态注册(Manifest 中) | |
|---|---|---|
| 注册方式 | registerReceiver(receiver, filter) | <receiver> 标签声明 |
| 生命周期 | 跟随 Activity/Service,必须手动 unregisterReceiver | 随应用安装生效,App 未启动也能接收 |
| Android 8.0+ 限制 | 大部分广播仍可用 | 绝大多数隐式广播不再发送给静态注册的 Receiver |
| 典型场景 | App 在前台时感知事件 | 开机自动启动(仅少数豁免广播) |
动态注册示例
class MainActivity : AppCompatActivity() {
private lateinit var receiver: BroadcastReceiver
private var isRegistered = false
override fun onResume() {
super.onResume()
receiver = MyReceiver()
val filter = IntentFilter().apply {
addAction(Intent.ACTION_BATTERY_LOW)
addAction(Intent.ACTION_BATTERY_OKAY)
}
registerReceiver(receiver, filter)
isRegistered = true
}
override fun onPause() {
super.onPause()
if (isRegistered) {
unregisterReceiver(receiver)
isRegistered = false
}
}
}动态注册的金科玉律:在哪注册就在对称位置注销——onResume 注册、onPause 注销是最常见的模式。忘了注销 = 泄漏 BroadcastReceiver,系统会在日志里警告你。
静态注册示例(仅在少数场景有效)
<receiver
android:name=".BootReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>开机广播 BOOT_COMPLETED 是少数几个在 Android 8.0+ 仍能通过静态注册接收的广播之一。
动手看:一条广播从"发出"到"被所有接收者处理"
猜一猜:同一个 App 里注册了 3 个 Receiver 监听同一个广播,它们接收的顺序是什么?
① 系统事件触发广播
系统检测到电池电量低于 15%,BatteryService 构造一个
Intent(Intent.ACTION_BATTERY_LOW),调用 sendBroadcast(intent)。这个
Intent 的 action 就是 BATTERY_LOW。
第 1 / 4 步 · ① 发送者 App 调 sendBroadcast(intent)(action = 自定义如 DATA_LOADED,或系统广播如 BOOT_COMPLETED)
点击播放,看一条广播如何从发送者发出、经系统总线收集注册者、并行分叉(fan-out)到多个 Receiver、最后触发各自 onReceive;可暂停、单步、拖进度逐帧观察。
代码逐段拆解:自定义广播与 App 内通信
发送自定义广播
// 发送方
val intent = Intent("com.example.app.DATA_LOADED").apply {
putExtra("record_count", 42)
`package` = packageName // Android 8.0+ 推荐:限制广播只在本 App 内接收
}
sendBroadcast(intent)接收自定义广播(动态注册)
// 接收方(在 Activity/Fragment 中)
val filter = IntentFilter("com.example.app.DATA_LOADED")
registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val count = intent.getIntExtra("record_count", 0)
updateUI(count)
}
}, filter)有序广播的使用场景
// 发送有序广播
sendOrderedBroadcast(
Intent("com.example.ACTION_CHECK"),
null, // 无需额外权限
null, // 结果接收者(可选)
null, // Handler(可选)
Activity.RESULT_OK, // 初始结果码
"OK", // 初始结果数据
null // 初始结果附加数据
)
// 在 Receiver 中处理并传递结果
class CheckReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (checkFailed()) {
resultCode = Activity.RESULT_CANCELED
resultData = "Check failed at step 1"
}
// 如果没调 abortBroadcast(),下一个 Receiver 继续执行
}
}有序广播的经典场景:级联校验——Receiver A 检查权限 → Receiver B 检查网络 → Receiver C 才开始实际工作。任何一环失败可阻断后续。
容易踩的坑
小结
BroadcastReceiver是 Android 发布-订阅模式的核心实现:系统/App 发送广播,注册了匹配 IntentFilter 的 Receiver 接收处理- 动态注册(代码中
registerReceiver)灵活但需手动注销;静态注册(Manifest 中声明)Android 8.0+ 对隐式广播有严格限制 sendBroadcast并行分发,顺序不保证;sendOrderedBroadcast按 priority 串行执行,可逐级传递结果或中断LocalBroadcastManager已被废弃——App 内通信推荐用 LiveData / Flow / EventBus 等现代方案- App 进入 Standby 模式后,后台的广播接收和网络请求会被系统延迟批处理,这是 Android 6.0+ 的 Doze 机制
练习
问题 1:“第28章 Broadcast Intents”覆盖哪些正式节点和项目主线?
问题 2:怎样建立本页最小可执行实验?
问题 3:为什么只在正常点击路径运行不能证明完成?
问题 4:怎样设计能推翻当前实现的反例?
问题 5:从第4版语境迁移到现代目标SDK时如何控制变量?
问题 6:本页达到独立交接标准需要什么?
名词解释
名词解释
本章出现的专业名词,用大白话再讲一遍。
- BroadcastReceiver(广播接收器)
Android 四大组件中最轻量的一个——它的唯一工作就是接收广播并处理。你可以把它理解成一个"事件订阅器":声明你对哪些事件感兴趣,事件发生时系统调你的
onReceive()来通知你。这是 Android 发布-订阅模式的核心实现。详见本章"什么是 BroadcastReceiver"一节。- IntentFilter
一组过滤规则,声明 BroadcastReceiver 关心哪些广播。通过
addAction()添加 action 字符串来匹配广播。系统通过对比广播的 action 与 IntentFilter 中声明的 action 来决定是否唤醒你的 Receiver。详见本章"动态注册示例"一节。- 有序广播(Ordered Broadcast)
一种按接收者优先级串行分发的广播模式。接收者通过
android:priority从高到低一个接一个执行。每个接收者可以修改结果数据传递给下一个,也可以完全截断广播(abortBroadcast())。适合"级联校验"等需要顺序控制的场景。详见本章"有序广播"一节。- 隐式广播(Implicit Broadcast)
不以特定包名为目标的广播——系统把 Intent 发给所有声明了匹配 IntentFilter 的 Receiver。Android 8.0+ 大幅限制隐式广播,绝大多数不再唤醒静态注册的 Receiver。这是为省电设计的举措。详见本章"容易踩的坑"一节。
“broadcast intent”不使用未获授权的纸书正文;InformIT出版信息与授权电子版完整目录只用于确认第四版32章、269个正式目录节点和时代语境,第四版官方勘误用于识别工具链变更。下列中文解释、图示、交互、代码与练习均为独立教学重写,平台行为再以Android Developers的一手文档复核。
为什么“broadcast intent”必须回到可观察状态
“broadcast intent”的学习结果不是记住类名,而是能预测“理解 Android 的广播机制:系统广播、自定义广播、动态注册与静态注册的区别,以及 Android 8.0+ 对隐式广播的限制和现代替代方案。”在一次输入、一次重建和一次失败中的不同状态,并指出哪条Android合同产生差异。
第四版机制逐项深读
28. Broadcast Intents
在“broadcast intent”中,“28. Broadcast Intents”把事件发送给匹配Receiver;静态与动态注册、导出权限和接收器短生命周期决定可达性,长任务应转交受约束后台工作。
Regular Intents vs Broadcast Intents
在“broadcast intent”中,“Regular Intents vs Broadcast Intents”把事件发送给匹配Receiver;静态与动态注册、导出权限和接收器短生命周期决定可达性,长任务应转交受约束后台工作。
Filtering Foreground Notifications
在“broadcast intent”中,分析“Filtering Foreground Notifications”先冻结设备API、targetSdk和输入,再沿回调与数据流寻找首个状态分叉,不能只描述最终页面。
Receivers and Long-Running Tasks
在“broadcast intent”中,“Receivers and Long-Running Tasks”跨应用边界时只授予完成任务所需的URI与组件权限,并在日志中移除联系人等个人信息。
For the More Curious: Local Events
在“broadcast intent”中,“For the More Curious: Local Events”把事件发送给匹配Receiver;静态与动态注册、导出权限和接收器短生命周期决定可达性,长任务应转交受约束后台工作。
For the More Curious: Limitations on Broadcast Receivers
在“broadcast intent”中,“For the More Curious: Limitations on Broadcast Receivers”把事件发送给匹配Receiver;静态与动态注册、导出权限和接收器短生命周期决定可达性,长任务应转交受约束后台工作。
For the More Curious: Detecting the Visibility of Your Fragment
在“broadcast intent”中,“For the More Curious: Detecting the Visibility of Your Fragment”的容器替换不是简单换画面;状态保存后提交、重复tag与嵌套管理器都会改变恢复结果。
“broadcast intent”验收回顾
“broadcast intent”只有在Intent合同、解析目标、权限、返回结果和重复副作用能够从相同基线再次得到相同断言时才通过;第四版机制与现代平台政策分别记录,不用新API名称掩盖旧行为。
← 上一页:WorkManager · 下一页:网页浏览 →
章专属可重放状态实验
先预测“前后台切换、动态注册、发送广播与进程回收”发生后,BroadcastReceiver、注册作用域与 WorkManager应怎样改变action、extras、导出/权限、接收窗口和转交工作;再操作三个实验。第四版示例与当前 Android 政策分别记录,实验不把新 API 名称倒填为原书内容。
实验一:所有者—状态—结果合同
选择任一正式目录节点和正常/边界场景,检查它是否真的进入本章状态合同。目录标题只有同时出现在解释、可视状态和交付证据中才算覆盖。
Owner · state · observable result
broadcast intent:状态合同
区分普通 Intent 与广播,并把长任务从 Receiver 转交受约束工作
验证场景
第四版正式目录节点
broadcast-intents · 正常任务
28. Broadcast Intents:固定 SDK、设备配置和初始状态,触发“前后台切换、动态注册、发送广播与进程回收”
冻结入口:28. Broadcast Intents
记录BroadcastReceiver、注册作用域与 WorkManager的初始action、extras、导出/权限、接收窗口和转交工作
观察:注册日志、发送方 UID、权限、onReceive 时长和转交 WorkInfo中的“28. Broadcast Intents”轨迹
预期:由BroadcastReceiver、注册作用域与 WorkManager提交action、extras、导出/权限、接收窗口和转交工作,并持续满足“Receiver 只做短处理,外部广播受权限与导出边界约束”
实验二:事件与生命周期轨迹
沿五次转换逐步执行“前后台切换、动态注册、发送广播与进程回收”。每一步只允许BroadcastReceiver、注册作用域与 WorkManager按职责提交状态,并持续核对“Receiver 只做短处理,外部广播受权限与导出边界约束”。
Deterministic event replay
broadcast intent:事件轨迹
不变量:Receiver 只做短处理,外部广播受权限与导出边界约束
交付证据:注册日志、发送方 UID、权限、onReceive 时长和转交 WorkInfo
实验三:章专属反例与同输入恢复
注入“在 onReceive 中同步联网,超过生命周期窗口被系统终止”,保存第一个偏离点;撤销后以完全相同的 SDK、设备状态和用户事件重放。只有注册日志、发送方 UID、权限、onReceive 时长和转交 WorkInfo一起恢复才算修复。
Fault · cancel · restore
broadcast intent:反例与恢复
故障:在 onReceive 中同步联网,超过生命周期窗口被系统终止
第 1 次使用相同 SDK、设备配置、初始状态与用户事件
保持正常输入不变,仅注入“在 onReceive 中同步联网,超过生命周期窗口被系统终止”
Receiver 只做短处理,外部广播受权限与导出边界约束
注册日志、发送方 UID、权限、onReceive 时长和转交 WorkInfo