EventBus是 Android 中一种常用的事件发布-订阅模式库,广泛用于组件之间的解耦通信,比如:

  • Activity 与 Fragment 之间通信
  • Service 与 UI 通信
  • 多个模块之间传递数据、消息、状态等

如果你是刚入门,可以从以下几个方面来学习EventBus(以 greenrobot 的 EventBus 为例,这是最常用的版本):

🧩 一、EventBus 的基本原理

核心思想
发送者(post)不需要知道接收者是谁;接收者注册后即可收到特定类型的事件。

通过三个步骤完成通信:

  1. 注册(register)
  2. 发送事件(post)
  3. 接收事件(订阅方法)

🛠 二、快速上手示例

1. 添加依赖

// build.gradle (app)
implementation 'org.greenrobot:eventbus:3.3.1'

2. 定义事件类

data class MessageEvent(val message: String)

3. 注册 & 注销 EventBus

在需要接收事件的类中:

override fun onStart() {
    super.onStart()
    EventBus.getDefault().register(this)
}
 
override fun onStop() {
    super.onStop()
    EventBus.getDefault().unregister(this)
}

4. 订阅事件(方法名随意,但必须带注解)

@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(event: MessageEvent) {
    Log.d("EventBus", "收到消息:${event.message}")
}

5. 发送事件

EventBus.getDefault().post(MessageEvent("Hello from another component"))

🧵 三、线程模式(ThreadMode)

@Subscribe(threadMode = ThreadMode.XXX)
模式说明
POSTING默认模式,在哪个线程发的,就在哪个线程回调
MAIN回调在主线程,适合更新 UI
MAIN_ORDEREDMAIN
类似,但有顺序保证
BACKGROUND如果发帖线程是主线程,则开新线程;否则复用当前线程
ASYNC无论如何都在新线程执行,适合执行耗时操作

💡 四、常见注意事项

  • 接收方法必须是public,且只有一个参数
  • 不要忘了registerunregister,否则会导致内存泄漏或收不到消息
  • 尽量避免事件粘滞(Sticky)除非你知道自己在做什么

🧪 示例场景

比如你有两个 Activity,A 启动 B,B 关闭时要向 A 传递一个结果消息,可以用EventBus

// AActivity.kt
override fun onStart() {
    super.onStart()
    EventBus.getDefault().register(this)
}
 
@Subscribe(threadMode = ThreadMode.MAIN)
fun onResultEvent(event: MessageEvent) {
    textView.text = "来自B的消息:${event.message}"
}
 
// BActivity.kt
EventBus.getDefault().post(MessageEvent("我从B回来了"))
finish()

📚 进阶点

  • 粘性事件postSticky() + registerSticky()
  • 使用EventBusBuilder 自定义配置
  • 替代方案对比:LiveDataStateFlowRxBusSharedViewModel

问题

eventbus 这种和 handler 还有 redux 这种都是处理事件流的,他们之间有什么区别呢?为什么现在组内用 redux这种方式了?

Handler

核心理念: 处理线程间通信。 工作方式: Handler 是 Android Framework 内置的机制,主要用于解决主线程(UI 线程)和子线程之间的通信问题。在子线程中执行耗时操作后,需要将结果更新到 UI,这时就会通过 Handler 发送消息(Message)或 Runnable 到主线程的消息队列,由主线程的 Looper 来处理。 优点:

  • Android 内置: 无需额外库,是 Android 平台的基础。
  • 安全、高效: 专门设计用于处理线程安全问题,确保在主线程上正确更新 UI。 缺点:
  • 耦合度高: 发送方和接收方通常需要互相持有引用,增加了组件间的耦合。
  • 代码复杂: 如果有很多不同类型的消息,需要编写大量的 if/elseswitch 语句来处理,代码会变得冗长且难以维护。

2. EventBus (事件总线)

核心理念: 发布-订阅模式(Pub-Sub)。 工作方式: EventBus 是一种第三方库(如 Greenrobot EventBus),它提供一个全局的事件中心。组件可以向这个中心“发布”事件,而对该事件感兴趣的组件则可以“订阅”这个事件。发布者和订阅者之间彼此不知道对方的存在,实现了完全的解耦。 优点:

  • 高度解耦: 组件之间不需要直接引用,发送者和接收者完全独立,降低了耦合。
  • 简化通信: 解决了不同组件(如 Activity、Fragment、Service)之间的复杂通信问题。
  • 线程切换方便: 很多 EventBus 库都支持配置事件处理的线程模式,可以轻松地在后台线程发布事件,然后在主线程处理。 缺点:
  • 难以追踪: 随着应用的增大,一个事件可能被多个地方发布或订阅,导致事件流难以追踪,调试起来非常困难。
  • 缺乏状态管理: EventBus 更多是传递事件,而不是管理状态。它没有一个集中的状态容器,无法确保应用状态的可预测性。

3. Redux (或类似的状态管理库)

核心理念: 单向数据流和单一可信源(Single Source of Truth)。 工作方式: Redux 源自于 React 社区,但其思想被广泛应用于其他平台。它有一个全局唯一的 Store,包含了整个应用的状态。应用的所有状态都存放在这里。当需要修改状态时,只能通过派发(Dispatch)一个 Action。这个 Action 会被一个或多个Reducer 函数接收,Reducer 是一个纯函数,它接收旧状态和 Action,然后返回一个新状态。所有 UI 组件都订阅这个 Store 的状态变化。 优点:

  • 可预测性: 状态变化是可预测的,因为状态是只读的,只能通过纯函数 Reducer 来改变。
  • 可追溯性: 整个应用的状态变化流程清晰可见:View -> Action -> Reducer -> Store -> View。这让调试变得非常容易,甚至可以实现“时间旅行”调试,回溯应用的历史状态。
  • 集中式状态管理: 所有状态都在一个地方管理,避免了状态分散、不一致的问题。
  • 易于测试: Reducer 是纯函数,输入确定,输出也确定,非常容易编写单元测试。 缺点:
  • 样板代码多: 相比 EventBus,Redux 通常需要更多的样板代码(Actions, Reducers, Store 配置等),特别是在小型项目中,可能显得有些“大材小用”。
  • 学习曲线: 相比传统的事件处理方式,Redux 的概念(纯函数、不可变状态、单向数据流)需要一定的学习成本。

为什么现在组内用 Redux 这种方式?

你的团队可能选择 Redux(或类似的思想,如 MVI - Model-View-Intent)来处理事件流,主要是为了解决大型和复杂应用中的以下痛点:

  1. 状态管理混乱: 随着应用功能的增加,状态会分散在不同的 Activity、Fragment 和 ViewModel 中,导致状态同步变得异常困难。比如,一个用户操作可能需要同时更新多个 UI 界面,而使用 EventBus 可能会导致复杂的事件链和难以追踪的逻辑。Redux 的单一可信源彻底解决了这个问题。
  2. 调试困难: 当应用出现 Bug 时,EventBus 导致的“广播式”通信模式使得很难确定一个状态变化的来源。你不知道哪个组件发布了事件,也不确定它被哪些组件订阅。而 Redux 的单向数据流和可追溯性使得调试变得非常高效。
  3. 代码可维护性: Redux 的严格范式(Action、Reducer、Store)强制开发者遵循一致的模式,使得代码结构清晰,新成员加入团队后能更快地理解和维护代码。特别是 Reducer 的纯函数特性,大大降低了逻辑的复杂性,减少了 Bug 的产生。
    总而言之,虽然 Handler 和 EventBus 在特定场景下仍有其价值,但在构建复杂的、状态密集的应用时,Redux 这种集中式、可预测、可追溯的状态管理模式能够提供更高的可维护性和稳定性。它将状态管理从混乱的“网状”通信转变为清晰的“单向”数据流,这对于团队协作和长期项目维护来说是巨大的优势。

MVI - Model-View-Intent 是什么

MVI,全称是 Model-View-Intent,是一种在 Android 开发中越来越流行的架构模式。它受到前端框架 Cycle.js 的启发,核心思想是建立一个单向、循环的数据流,从而实现应用状态的可预测性和可追溯性。

MVI 的三个组成部分各司其职:

  • Intent(意图):
    • 在 MVI 中,Intent 不是指 Android 里的 android.content.Intent
    • 它代表了用户的意图操作,例如“点击按钮”、“输入文本”、“刷新页面”等。
    • View(视图)将用户的这些操作包装成一个 Intent,然后发送出去。
  • Model(模型):
    • 在 MVI 中,Model 的概念与其他架构(如 MVVM)有些不同。它不仅仅是数据层,而是代表了应用当前的完整状态
    • 这个状态通常是一个不可变(Immutable)的数据对象,包含了所有 UI 需要展示的数据。
    • 当接收到一个 Intent 时,Model 会根据这个 Intent 和当前的状态,计算出一个新的状态。由于状态是不可变的,每次状态更新都是创建一个全新的状态对象,而不是修改旧的。
  • View(视图):
    • View 负责展示 UI,并接收用户的操作
    • 观察着 Model 的状态变化。当 Model 的状态更新时,View 会根据新的状态来重新渲染 UI。
    • 同时,当用户与 UI 交互时,View 会将这些操作发送成 Intent,作为数据流的起点。

MVI 的单向数据流

我们可以把 MVI 看作一个清晰的、可预测的循环:

用户操作 → View → Intent → Model → View 重新渲染

  1. View 捕获用户的操作(如点击事件)。
  2. 这些操作被封装成一个 Intent
  3. Intent 被发送给 Model
  4. Model(通常是在 ViewModel 或 Presenter 中实现)接收 Intent,并根据它和当前的状态,计算并生成一个新的状态
  5. Model 将这个新的状态发布出去。
  6. View 订阅了这个状态流,当接收到新状态时,重新渲染自己的 UI。
    这个循环确保了数据流的清晰和可追溯,所有状态变化都必须通过 Intent 这一唯一的入口。

MVI 的优势

  • 单一可信源(Single Source of Truth): 所有应用状态都集中在一个 Model 中,避免了状态分散和同步问题。
  • 可预测性: 状态变化是可预测的,因为每次变化都由一个特定的 Intent 触发,并且状态是不可变的。这让调试变得非常容易。
  • 可追溯性: 整个数据流是单向的,你可以轻松地追踪一个 UI 变化是如何产生的,这对于发现和修复 Bug 至关重要。
  • 线程安全: 由于状态是不可变的,你可以安全地在任何线程中处理状态,而不用担心数据竞争问题。
  • 易于测试: Intent 和 Reducer(计算新状态的纯函数)是独立且可测试的,这使得单元测试变得非常简单。

总的来说,MVI 架构的核心思想与 Redux 非常相似,都是通过单向数据流不可变状态来解决复杂应用的状态管理问题。因此,如果你的团队正在使用 Redux 类似的模式,那么 MVI 也是一个非常自然且强大的选择。

更新: 2025-08-05 17:09:55
原文: https://www.yuque.com/dongpozhouzi-mshe3/zhm85g/gv7pzfbksufdi24m


相关笔记