Android 里的"事件总线"现在已经不是一个框架通吃的事了,得按场景拆:EventBus(GreenRobot) 是老牌霸主,LiveData / Kotlin Flow(SharedFlow) 是 Jetpack 时代 Google 主推的替代,RxBus 是 RxJava 时代的产物,现在基本被 Flow 取代。下面把主流方案摊开讲。
主流框架一览
1️⃣ EventBus
最经典的事件总线,基于注解 + (默认)反射,3.x 后可用 APT 编译期生成索引把注册耗时从 ~1-5ms 压到 ~0.01ms。
- 优点:上手极简、一对多广播、跨模块天然解耦、粘性事件原生支持、线程模式(
MAIN/POSTING/BACKGROUND/ASYNC)齐全 - 缺点:不感知生命周期,忘 unregister 就泄漏;事件链路隐式,调试时很难追"谁发的、谁收的";无背压
- 典型场景:登录/登出广播、全局配置变更、网络状态切换、跨模块无依赖通信
2️⃣ LiveData(Jetpack)
严格说它不是"事件总线",是生命周期感知的状态持有者,但常被拿来替代 EventBus 做组件内通信。
- 优点:自动感知 Lifecycle,不会在 destroyed 后回调;与 ViewModel 搭配是 Google 官方 MVVM 标配
- 缺点:天然粘性(新 Observer 会收到上次的值)→ 数据倒灌问题,得用
SingleLiveEvent/UnPeekLiveData绕;线程只能主线程回调(postValue半自动切);操作符贫弱(map/switchMap就这点) - 典型场景:ViewModel → View 的状态驱动(列表数据、Loading 状态等)
3️⃣ Kotlin Flow(SharedFlow / StateFlow)
目前最推荐的新项目方案,协程体系下的响应式流。
| 维度 | EventBus | LiveData | StateFlow / SharedFlow |
|---|---|---|---|
| 生命周期感知 | ❌ 手动 | ✅ 自动 | ✅ repeatOnLifecycle |
| 线程切换 | ✅ ThreadMode | 仅主线程 | ✅ flowOn+ Dispatcher |
| 粘性 | ✅ | ✅(天然) | StateFlow 粘性,SharedFlow 可配 replay |
| 背压 | ❌ | ❌ | ✅ buffer/conflate |
| 操作符 | ❌ | ⚠️ 极少 | ✅ 丰富 |
| 调试追踪 | ❌ 隐式 | ✅ 明确持有者 | ✅ 明确流链路 |
性能上 Pixel 4 实测每秒 100 次简单事件:EventBus 延迟 ~12ms / CPU 8%,SharedFlow ~9ms / CPU 6%,LiveData 扛不住高频。
- StateFlow ≈ LiveData 替代品(总是持有最新值,冷流)
- SharedFlow ≈ EventBus 替代品(多订阅者、可配 replay、一次性事件用
replay=0 extraBuffer=0)
4️⃣ RxBus(RxJava + Subject)
RxJava 时代的自制事件总线(BehaviorSubject / PublishSubject)。
- 功能最强(操作符全家桶、线程随意切),但体积大、学习曲线陡、易内存泄漏,新项目基本被 Flow 替代,只在存量 RxJava 项目里还能见到。
- 在Java项目种使用,部分场景下可用CompletableFuture 代替。
EventBus与RxJava对比
如果一个订阅者需要注册多个事件的时候,Rxjava需要一个个单独的注册,而EventBus则可以实现一个订阅者订阅多个事件,和一个事件对应多个订阅者。所以在Rxjava的基础上有了Rxbus来作为事件总线的库。RxBus在订阅者数量很大的情况下性能下降很明显,比不过EventBus。 RxAndroid 可理解为专供Android平台的RxJava,针对平台增加了少量类。
CompletableFuture 与RxJava对比
| 维度 | CompletableFuture | RxJava |
|---|---|---|
| 抽象模型 | 一个异步结果(Promise) | 一个事件流(Stream) |
| 数据量 | 单个值 / 异常 | 0~N 个值 + 完成 / 异常 |
| 消费方式 | 一对一 | 一对多 |
| 是否可复用 | ❌ 一次性的 | ✅ 可重复订阅 |
| 是否支持事件总线 | ❌ 不适合 | ✅ 天生适合 |
| 背压 | ❌ 无 | ✅ 有 |
| 线程调度 | 基础 | 极其精细 |
5️⃣ LocalBroadcastManager
已废弃,别用了,反射+Intent 性能差、类型不安全。
选型决策树
💡 一句话原则:能用 Flow/LiveData 就别上 EventBus,EventBus 只留给"跨模块全局广播"这一窄场景
- 同一页面 / 同一模块内 View → VM → View → ViewModel + LiveData 或 StateFlow
- 父子 Fragment 通信 → Fragment Result API / Navigation SavedStateHandle(不是事件总线)
- 一次性事件(Toast、导航、Snackbar)→ SharedFlow(replay=0) 或 Channel
- 跨模块、无依赖、一对多广播(登录态、埋点、配置推送)→ EventBus 仍是最省事的解法
- 高频事件 / 需要背压 / 复杂数据流变换 → Flow
- 存量 RxJava 项目 → RxBus 继续用,新模块切 Flow
一个常见的坑
LiveData 做"事件类"数据(导航、Toast)会数据倒灌——屏幕旋转后 Observer 重新注册,又会收到上次那次导航事件,页面跳两次。解决办法:
- 老方案:
SingleLiveEvent(但只支持一个 Observer,坑) - 改进:
UnPeekLiveData(允许多 Observer,只消费一次) - 彻底解:换 SharedFlow,
replay=0,天然单次,没这问题