做安卓开发有些年头的人,大概都对 EventBus 有过 “真香” 时刻。早几年的安卓项目里,greenrobot 的 EventBus 几乎是标配,但凡涉及跨组件传消息,第一反应就是掏它出来。
但现在行业里的态度早已经反转:新项目基本不引入,老项目忙着重构移除,它渐渐成了大家口中 “过时、坑多、不推荐” 的代表。
很多人纳闷:EventBus 本身没出大 bug,功能也没退化,怎么就突然被嫌弃了?
一、当年的 EventBus,凭什么成了安卓标配?
要理解它的火,得先知道早年安卓开发的组件通信有多折磨人。
Activity、Fragment、Service、BroadcastReceiver 各有各的生命周期,各有各的启动规则,偏偏业务逻辑总需要跨组件传话。就拿最常见的登录场景说,用户点完登录成功,首页要刷数据、个人中心要更新信息、购物车要刷新状态、消息页要改角标…… 这一圈通知下来,能把人写吐。
不用 EventBus 的话,翻来覆去就那几种笨办法:
- 写接口回调,一层一层往下传,从 Activity 传到 Fragment,再从 Fragment 传到 Adapter,嵌套深了自己都能绕晕;
- 用 LocalBroadcastManager 发广播,代码啰嗦不说,传个数据还要序列化,麻烦得很;
- 用 Handler 发消息,持有引用怕内存泄漏,不持有引用又发不到目标;
- 干脆直接强转上下文,耦合到妈都不认。
就在这时候 EventBus 横空出世了。
一行代码发事件:
EventBus.getDefault().post(new LoginSuccessEvent());
哪里需要哪里加个注解订阅:
@Subscribe
public void onLoginSuccess(LoginSuccessEvent event) {
refresh();
}
完事了。
发送方不用管谁会收,接收方不用管是谁发的,中间全靠 EventBus 中转。解耦、简洁、上手快,几行代码就能搞定以前几十行的活儿,对于当年那个遍地回调、耦合严重的开发环境来说,体验简直是降维打击。也难怪它迅速火遍整个安卓圈,成了大小项目的必备依赖。
二、坑都是慢慢踩出来的:隐式依赖是最大的雷
项目小的时候,怎么用都爽;代码一多、团队一扩,问题就一个个冒出来了。
首当其冲的就是隐式依赖。
你接手一个老项目,看到一行 EventBus.getDefault().post(new LoginSuccessEvent()),想知道这行代码执行完会触发什么操作?对不起,代码上半毛钱看不出来。你只能全局搜 LoginSuccessEvent 这个类,然后翻遍所有带 @Subscribe 注解的方法,才能搞清楚原来有七八个页面和模块都在监听这个事件。
代码表面上,LoginManager 和 HomeFragment、MineFragment 没有任何依赖关系,但实际上它们通过 EventBus 紧紧绑在了一起。这种 “存在但看不见” 的依赖,就是大型项目维护的噩梦 —— 你改一个事件,根本预估不到会影响多少地方;删一个订阅者,也不知道会不会漏掉什么逻辑。
比隐式依赖更头疼的,是调用链完全不可追踪。
比如出了个 bug:用户登录成功后,首页突然开始卡顿。你顺着 login 方法往下查,查到一行 post 事件,线索直接就断了。后面到底触发了多少逻辑?哪个步骤导致的卡顿?你根本没法顺着调用栈往下走,只能一个订阅者一个订阅者地去翻。
正常的代码逻辑是 A→B→C→D,链路清晰,打个断点就能跟到底。到了 EventBus 这儿就变成了 A 发事件,然后一堆不知道在哪的订阅者各自执行,整个调用链直接碎成了渣。大型项目里事件成百上千,排查问题的效率能低到令人崩溃。
三、安卓平台独有的致命伤:生命周期失控
在安卓平台上,EventBus 还有个绕不开的原生问题:它完全不管组件生命周期。
安卓的 Activity 和 Fragment 说销毁就销毁,但 EventBus 本身不感知这些。你得自己在 onStart 里注册,onStop 里解绑,忘一步都是坑。
忘了 unregister?EventBus 会一直持有 Activity 的引用,垃圾回收收不掉,直接内存泄漏。更要命的是,页面已经销毁了,事件过来还去执行 UI 更新,直接就给你崩了。
开发者不仅要管业务逻辑,还要自己盯着注册、解绑、生命周期、线程切换,稍不留神就踩坑。项目小的时候还能靠规范约束,人一多、代码一杂,出问题就是早晚的事。
还有线程问题,看着方便,实则混乱。EventBus 提供了 POSTING、MAIN、BACKGROUND、ASYNC 几种线程模式,一个注解就能指定执行线程。可项目一大就乱了:同一个事件,有的订阅者切主线程,有的切后台,有的直接在发送线程执行。
出了 UI 卡顿、数据竞争、重复执行之类的问题,你根本没法一眼判断出这段逻辑到底跑在哪个线程上,排查复杂度直接翻倍。
四、不是 EventBus 不行了,是开发思路变了
其实平心而论,不是 EventBus 变差了,而是安卓开发的整个思路变了。
早年的安卓开发,大家还在摸索阶段,怎么方便怎么来,能快速实现功能就是王道。但随着项目越来越大、团队越来越多,行业开始追求可维护、可追踪、可管控,官方也慢慢推出了一套完整的架构组件:ViewModel、LiveData、StateFlow、Lifecycle…… 单向数据流的思路逐渐成了主流。
EventBus 是典型的广播思维:登录成功了,发个事件告诉大家,大家收到了自己该干嘛干嘛。 而现代安卓架构是状态思维:用户的登录状态变成了已登录,各个 UI 层观察这个状态,自动渲染对应的界面。
一字之差,天壤之别。
用 StateFlow 举个例子,ViewModel 里维护一个用户状态:
class UserViewModel : ViewModel() {
private val _userState = MutableStateFlow<UserState>(UserState.Loading)
val userState = _userState.asStateFlow()
}
UI 层直接订阅:
lifecycleScope.launch {
viewModel.userState.collect { state ->
render(state)
}
}
谁持有状态、谁观察状态、状态变化会触发什么逻辑,一目了然。依赖关系明明白白写在代码里,调用链清晰可追溯,还自带生命周期感知,页面销毁了自动停止订阅,不会泄漏也不会崩。
EventBus 的那些优势,到了这儿全被替代了;而 EventBus 的那些坑,这儿全没有。
更糟的是,EventBus 用多了很容易陷入 “事件地狱”。项目刚开始的时候,就 LoginEvent、LogoutEvent、RefreshEvent 寥寥几个,清爽得很。可随着业务迭代,今天加个头像更新事件,明天加个购物车变化事件,后天加个全局配置变更事件…… 半年下来,项目里塞了几十上百个 Event 类,什么逻辑都想靠发事件解决。
到最后 EventBus 就成了个 “垃圾通信总线”,大家遇到跨组件的问题就发个事件扔出去,至于谁接收、怎么处理、有没有重复,没人说得清。很多事件到最后为什么存在、还有没有人用,都没人敢动。
五、EventBus 真的彻底不能用了吗?
当然也不是。它只是不适合写普通业务逻辑,不是一无是处。
比如真正的全局广播场景:App 强制退出登录、全局语言切换、夜间模式切换…… 这些本来就是 “一个事件发生,多个模块都可能关心” 的场景,用 EventBus 的模型反而很合适。
再比如插件化、高度模块化的系统,模块之间不想直接依赖,用事件作为通信协议,也是合理的选择。
但绝大多数业务场景,比如修改头像刷新个人中心、下单成功刷新购物车,真的别再用 EventBus 了。这些本质上是 “数据状态发生了变化”,而不是 “发生了一次性事件”,把状态统一放到 Repository 里,各个页面通过 ViewModel 观察状态自动更新,比发一堆事件靠谱得多。
六、最后说句实在话
EventBus 的崛起,是因为它解决了当年安卓组件通信的痛点,用最简单的方式实现了解耦,是那个时代的优秀方案。
它被嫌弃,也不是因为它做错了什么,而是因为它最大的优点 —— 无拘无束的通信自由,在大型工程里最终变成了最大的缺点:谁都能发,谁都能收,到最后没人说得清到底谁在和谁通信。
安卓架构这么多年的演进,其实就是从 “追求开发效率” 走向 “追求维护效率” 的过程。从方便到可控,从灵活到规范,EventBus 的起落,刚好就是这个过程最真实的缩影。