
Android 基础补强 B09BroadcastReceiver收到通知之后为什么不能直接做长任务摘要用应用内部的广播练习注册范围、导出属性和接收时机再理解现代系统的隐式广播限制及广播与持久状态的区别。标签Android、BroadcastReceiver、广播、生命周期、第一行代码本文对应《第一行代码》第 3 版第 6 章补充 28 天项目课程中较少单独展开的四大组件基础。书中的广播示例适合理解消息分发移到现代系统上还需要核对注册方式、目标版本和来源限制。下文示例为教学练习未在当前工程和设备组合上编译运行。1. 广播通知一件事不能替代数据本身假设后台数据同步完成可以发送一个事件告诉相关组件“数据可能已变化”。收到事件后页面仍应从 Repository 查询当前数据而不是把广播当作唯一事实来源。页面未注册时可能错过事件进程也可能结束重新进入页面时依靠数据库恢复才能解释最终显示从哪里来。同一个进程内的收藏页面通常可以直接观察 Flow没有必要为了“用到广播”绕一圈。学习广播的价值是理解系统与组件之间的事件分发知道什么时候需要监听系统状态以及为什么事件通知和长期存储承担不同责任。广播有清单注册与运行时注册。面向 Android 8.0 及以后目标版本的应用许多隐式广播不能继续在清单中随意注册具体广播还有例外与专门限制。不能把旧书中的任意 action 原样复制后就假设所有设备都拥有相同触发行为。Android 广播指南发送方式还要区分普通广播与有序广播。sendBroadcast不保证接收顺序接收器不能借此向后续接收器传递处理结果也不能中止其他接收器。sendOrderedBroadcast逐个交付接收器可沿链传递结果并在广播允许中止时终止后续交付。同一进程内可按匹配过滤器的优先级从高到低排序同优先级不保证先后。清单或动态注册是注册维度普通或有序是发送维度不能混成一组互斥类别。广播发送方式Android 16 对所有应用调整了优先级范围android:priority与IntentFilter.setPriority()不能再保证跨进程的广播交付顺序只在同一应用进程内尊重优先级即使属于同一个应用拆成多个进程也不能依赖该排序。应用优先级还会被限制在系统保留最高、最低值之间。因此不能把“甲进程先写、乙进程后读”建立在广播优先级上应另用明确的进程间协调协议。Android 16 广播优先级行为变化2. 用仅限自己应用的事件做最小实验本例在 Activity 可见期间接收应用内部练习广播。使用 AndroidXContextCompat.registerReceiver并声明不导出只适合本例“本应用发送”的来源约定。若监听其他应用或某些高权限系统组件的广播必须重新核对导出与权限需求不能机械使用相同标记。以下代码放在练习 Activity 中viewModel.onDataMayHaveChanged()仅发起状态核对不在回调里直接执行耗时磁盘或网络操作。省略常规导包、布局与 ViewModel 初始化。privatevalactionDataChangedcom.example.devcommunity.DATA_CHANGEDprivatevarregisteredfalseprivatevalreceiverobject:BroadcastReceiver(){overridefunonReceive(context:Context,intent:Intent){if(intent.actionactionDataChanged){viewModel.onDataMayHaveChanged()}}}overridefunonStart(){super.onStart()ContextCompat.registerReceiver(this,receiver,IntentFilter(actionDataChanged),ContextCompat.RECEIVER_NOT_EXPORTED)registeredtrue}overridefunonStop(){if(registered){unregisterReceiver(receiver)registeredfalse}super.onStop()}privatefunsendPracticeEvent(){sendBroadcast(Intent(actionDataChanged).setPackage(packageName))}注册与注销成对出现且使用同一个 Receiver 实例。本例的兴趣范围是可见期间因此对应onStart与onStop。若选择其他范围要根据实际需求解释为什么不能在 Activity 注册后永不注销让更长的注册关系保留短寿命 Context。setPackage限定发送目标范围非导出标记限制这个接收器的外部可达性。action 字符串很长或看上去随机都不构成权限机制。真正开放给其他应用的接收器应核验来源和参数必要时使用权限保护不能把所有收到的 extras 当作可信数据。3. onReceive 的寿命不是后台任务的寿命接收回调应该迅速完成。直接在其中访问网络、扫描大量文件或执行长计算会拖慢相关线程也无法获得可靠的长期执行保证。把工作随手丢给一个裸线程后返回进程也不因此被永久保活。goAsync()可以让异步处理延续一段受限的接收工作但仍有执行时间约束并且需要保证最终调用finish()。它不等于无期限后台服务。需要可靠、可延迟完成的持久任务应按业务选择适合的后台机制例如 WorkManager只刷新当前屏幕的状态则可以交由明确的屏幕作用域处理。BroadcastReceiver API特别容易混淆的是“系统发了事件”和“任务已完成”。前者只表示触发条件后者需要任务自己的持久状态、结果与重试规则。广播接收器可以决定是否安排工作但业务正确性不应依赖一次回调必然执行到底。4. 通过接收范围验证注册策略在练习 Activity 前台点击发送按钮预期接收一次事件并触发一次状态核对。切到其他页面让 Activity 停止后从自己应用的另一练习入口发送相同事件预期这个 Receiver 不接收返回页面时应主动读取当前数据不能要求刚才那条通知重新播放。再连续进入退出五次核对每次发送是否仍只收到一次确认没有累积注册。旋转后旧实例注销、新实例注册日志应能区分实例。若某个系统广播的表现不同记录系统版本、targetSdk、action 和注册方式再查对应文档不用一个内部广播实验推断所有系统行为。可在测试实现中让onReceive记录当前线程但不要通过长时间阻塞来“证明”限制。真正的实验应控制范围并恢复代码避免把练习故障留在正式入口。5. 原创面试问答与追问问一动态注册的广播能恢复错过的收藏变化吗不能把普通广播当作完整历史页面应重新读取当前事实。追问持续一致性怎么做用数据库和可观察查询保存并分发状态。问二不导出标记能用于所有系统广播吗不能一概而论部分系统来源来自不同 UID需要按官方契约选择。追问本例为什么可以它只接收自己应用发送的练习事件。问三goAsync 能执行任意长任务吗不能仍受广播执行窗口约束并需结束。追问可靠的延迟工作怎么处理交给合适的持久后台调度机制并记录任务状态。问答为原创自测可与面试鸭广播主题联用不是题库答案转载。6. 本专题验收能够解释发送方、action、接收范围、注册寿命和回调中的工作边界验证前台接收、停止后不接收、重复进入不重复注册能区分事件通知与数据库事实。书本第 6 章提供组件机制现代官方文档补上系统限制项目中再根据需要选择是否使用广播。