01 Broadcast 基础与分类

基于原 PDF 修订、联网校对并补充缺失内容

1. Broadcast 是什么

Broadcast 是 Android 中基于 Intent 的消息发布/订阅机制。发送方通过 Intent 表达“发生了什么”,接收方通过 IntentFilter 订阅感兴趣的 action。它既可以用于系统事件通知,也可以用于跨应用或应用内部的低耦合通知。

理解 Broadcast 时不要只按“无序/有序/本地”划分,更建议按三个维度看:作用范围、投递顺序、是否缓存最后一次事件。

维度 常见类型 重点
作用范围 系统广播、应用内广播、跨应用广播 由 Intent 的显式/隐式、package、permission、receiver exported 等共同决定。
投递顺序 普通广播、Ordered Broadcast 普通广播不保证接收顺序;有序广播可传递 result,也可被 abort。
是否缓存 非粘性广播、Sticky Broadcast 粘性广播会缓存 Intent 并重新投递给后注册者,但已被官方标为不推荐/废弃风险点。

2. 普通广播:sendBroadcast

普通广播使用 Context.sendBroadcast(intent)。它适合“通知型”事件:我只告诉大家某事发生了,不关心谁先收到,也不关心接收者返回什么。

  • 多个接收者之间没有可依赖的执行顺序。
  • 接收者不能把处理结果可靠地传给下一个接收者。
  • 如果广播是隐式的,任何匹配的接收者都有机会收到;不要用它传敏感数据。
// Java:发送一个应用内约定 action 的普通广播
Intent intent = new Intent("com.example.app.ACTION_SYNC_FINISHED");
intent.setPackage(context.getPackageName()); // 限定在本应用包内,避免隐式广播外泄
intent.putExtra("success", true);
context.sendBroadcast(intent);

3. 有序广播:sendOrderedBroadcast

原 PDF 写成 sendOrderBroadcast,这是方法名错误;正确 API 是 sendOrderedBroadcast。Ordered Broadcast 会按照优先级和系统调度顺序逐个投递,前一个接收者可以通过 setResultCode、setResultData、setResultExtras 修改结果,也可以 abortBroadcast 阻止后续接收者继续接收。

Intent intent = new Intent("com.example.app.ACTION_CHECK");
intent.setPackage(context.getPackageName());
context.sendOrderedBroadcast(
        intent,
        null,      // receiverPermission
        null,      // resultReceiver
        null,      // scheduler
        Activity.RESULT_OK,
        null,
        null
);
  • 适用:确实需要“前一个接收者影响后一个接收者”的场景。
  • 不适用:普通应用内部事件总线、频繁状态同步、UI 层状态通知。
  • 注意:有序广播是串行投递,滥用会影响性能和响应时间。

4. 显式广播与隐式广播

类型 写法 适用场景 风险/限制
显式广播 setPackage() 或 setComponent() 只发给指定应用或组件;内部通信;合作方 SDK 通信 更安全,但仍要校验来源与数据。
隐式广播 只设置 action,不指定包/组件 系统级事件通知;公开事件 可能被其他应用接收;Android 8.0 起 manifest 隐式广播受限。

实战建议:自定义业务广播尽量显式化。对外开放时使用自有包名前缀的 action,例如 com.example.pay.ACTION_RESULT,而不是 ACTION_RESULT 这种全局易冲突名称。

5. LocalBroadcastManager:只作为历史知识

原 PDF 把 LocalBroadcastManager 作为本地广播常规方案介绍,但现在它已被 AndroidX 官方完全废弃。它本质是应用内事件总线,并且使用 Intent 反而增加了不必要的限制。

  • 如果是 ViewModel 到 UI:使用 LiveData、StateFlow、SharedFlow。
  • 如果是模块间事件:优先定义清晰接口,必要时用 Kotlin Flow/RxJava/EventBus。
  • 如果是跨进程:LocalBroadcastManager 不适合;应考虑 Binder/AIDL、ContentProvider、Messenger 或系统广播。
// Kotlin:用 SharedFlow 替代 LocalBroadcastManager 的一个简单例子
object AppEvents {
    private val _events = MutableSharedFlow<AppEvent>(extraBufferCapacity = 1)
    val events: SharedFlow<AppEvent> = _events

    fun emit(event: AppEvent) {
        _events.tryEmit(event)
    }
}

sealed interface AppEvent {
    data class SyncFinished(val success: Boolean) : AppEvent
}

6. Sticky Broadcast:补充概念

Sticky Broadcast 会把最后一次 Intent 缓存在系统中,后续注册的 receiver 也可能收到这条历史 Intent。这个设计存在敏感数据泄漏、伪造和篡改风险,官方已经把 sticky broadcast 标为不推荐使用。

  • 不要在新业务中发送 sticky broadcast。
  • 需要“当前状态”时,把状态放入数据库、DataStore、Repository 或系统服务查询接口。
  • 系统广播中某些历史行为会表现得像 sticky,例如电量状态查询,但应用层不要仿造。

参考资料