news 2026/11/6 3:47:20

Android EventBus 入门:原理、使用与常见踩坑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android EventBus 入门:原理、使用与常见踩坑

一、为什么需要 EventBus?

开发 Android 应用时,经常会遇到组件之间传递消息的需求。

例如,一个商城应用中,商品页面负责“加入购物车”,而页面顶部的工具栏负责显示购物车数量。加入成功后,如何通知工具栏更新?

我们可以定义接口,通过回调传递结果。但随着组件增多,接口可能需要经过多层传递,维护起来越来越麻烦。

EventBus 提供了另一种方式:发布者发送事件,订阅者接收事件,由事件总线负责分发。

整个过程可以理解为:

商品 Fragment │ │ 发布“购物车数量变化”事件 ▼ EventBus │ │ 分发给订阅者 ▼ 宿主 Activity 更新工具栏

二、先理解三个核心概念

使用 EventBus,只需要先掌握三个角色。

角色作用示例
事件 Event描述发生了什么,以及相关数据购物车数量变为 3
发布者 Publisher发送事件商品 Fragment
订阅者 Subscriber接收并处理事件显示购物车数量的 Activity

事件通常是一个普通的数据类:

data class CartCountChangedEvent( val count: Int )

建议事件名称表达“已经发生的事情”,例如CartCountChangedEvent,比UpdateEvent更容易理解。

同时,事件中尽量只携带必要数据,避免放入 Activity、View 或 Context 等对象,以免延长这些对象的生命周期。

三、用一个例子完成消息传递

下面以“商品 Fragment 添加购物车,宿主 Activity 更新数量”为例。

这两个组件处于同一个前台页面中,Activity 接收事件时仍然可见。

1. 添加依赖

在应用模块的build.gradle.kts中添加:

dependencies { implementation("org.greenrobot:eventbus:3.3.1") }

2. 定义事件

新建一个 Kotlin 数据类:

data class CartCountChangedEvent( val count: Int )

这个类负责传递购物车的最新数量。

3. 注册订阅者

布局中假设已经存在 ID 为cartCountText的 TextView。

import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode class MainActivity : AppCompatActivity() { override fun onStart() { super.onStart() EventBus.getDefault().register(this) } @Subscribe(threadMode = ThreadMode.MAIN) fun onCartCountChanged(event: CartCountChangedEvent) { findViewById<TextView>(R.id.cartCountText).text = "购物车:${event.count}" } override fun onStop() { EventBus.getDefault().unregister(this) super.onStop() } }

这里有三个关键点:

  • register(this):让当前 Activity 开始接收事件。
  • @Subscribe:标记用于接收事件的方法。
  • unregister(this):停止接收事件,并释放注册关系。

注意点:

1. 订阅方法需要是public、返回Unit,并且只有一个参数

2. 参数类型决定它接收哪一种事件,方法名称本身不决定事件匹配。也就是说,把方法改名为handleCartEvent,仍然能够收到CartCountChangedEvent。

4. 发布事件

在商品 Fragment 中,加入购物车成功后发送事件:

import org.greenrobot.eventbus.EventBus // 在“加入购物车成功”的业务回调中执行。 // latestCount 应来自业务层返回或查询到的最新数量。 fun onAddToCartSuccess(latestCount: Int) { EventBus.getDefault().post( CartCountChangedEvent(count = latestCount) ) }

执行post()后,EventBus 会寻找匹配的订阅方法,并按照配置的线程模式进行调用。

四、ThreadMode:订阅方法运行在哪个线程?

Android 开发中,更新 UI 必须在主线程执行,因此线程模式是 EventBus 最需要理解的部分。

常见模式如下:

模式执行规则适用场景
POSTING在发布事件的线程执行,是默认模式简短、无 UI 操作的处理
MAIN主线程发布时直接执行;其他线程发布时切到主线程更新界面
MAIN_ORDERED始终排队到主线程执行希望避免主线程内立即嵌套调用
BACKGROUND主线程发布时交给后台线程;后台线程发布时直接执行非 UI 工作
ASYNC交给线程池执行需要异步执行的独立任务。全面外包

有两个地方很容易理解错。

第一,post()不代表自动切换到后台线程。

如果使用默认的POSTING,主线程发送事件,订阅方法就会在主线程执行。此时如果处理大量计算,就可能造成卡顿。

第二,BACKGROUND不保证每次都切换线程。

如果事件本来就在后台线程发布,它会直接在当前线程执行。

对于前面的购物车示例,订阅方法需要修改 TextView,因此选择:

@Subscribe(threadMode = ThreadMode.MAIN)

即使业务回调来自后台线程,界面更新也会在主线程进行。

不过,MAIN 只解决线程位置问题。订阅方法中仍然不能执行耗时操作,可能会导致ANR

五、粘性事件

普通事件有一个特点:

只有发送时已经注册的订阅者,才有机会收到事件。

例如:

先 post 事件 → Activity 后 register → 收不到之前的普通事件

如果业务确实需要“先发送,后订阅”,可以使用粘性事件。

发布方式:

EventBus.getDefault().postSticky( CartCountChangedEvent(count = 3) )

订阅方式:

@Subscribe( threadMode = ThreadMode.MAIN, sticky = true ) fun onCartCountChanged(event: CartCountChangedEvent) { // 更新界面 }

EventBus 会保存对应事件类型最近一次发布的粘性事件。新的粘性订阅者注册时,可以收到这份缓存。

这里要注意:

  • 同一种事件类型通常只保留最近一次粘性事件,不会保存完整历史。
  • 缓存在内存中,进程结束后会丢失。
  • 粘性事件不会在被接收后自动删除。

不再需要时,可以主动移除:

EventBus.getDefault().removeStickyEvent( CartCountChangedEvent::class.java )

六、生命周期与原理:理解后才能少踩坑

前面的示例使用onStart()注册,onStop()注销,意味着 Activity 只在可见期间接收事件。

这也带来一个需要留意的问题:

如果首页已经进入onStop(),用户在另一个 Activity 修改了数据,此时首页无法收到普通事件。返回首页后,它也不会自动补收。

因此,注册位置需要根据业务决定:

注册与注销位置接收范围
onStart()/onStop()页面可见期间
onCreate()/onDestroy()页面实例存活期间

还有需要注意的是,如果订阅者需要操作 Fragment 的视图,还需要考虑 Fragment 和视图的生命周期不同,避免访问已经销毁的 View。

还有两个常见问题需要记住。

忘记注销可能导致内存泄漏。EventBus 会持有已注册订阅者的引用,页面退出后如果仍然注册,就可能无法及时释放。

EventBus 默认不能跨进程通信。Android 中不同进程拥有各自的内存和 EventBus 实例。跨进程通信应使用 Binder、AIDL 等机制。

七、什么时候适合使用 EventBus?

EventBus 适合组件之间关系比较松散、需要广播通知的场景,例如多个模块监听某个业务操作完成。

但它也有代价:消息调用链变得不直观。查看一处post(),通常无法直接看出所有接收方,需要继续搜索订阅代码。

因此,选择通信方式时,可以参考下面的思路:

场景可以优先考虑
两个对象之间有明确调用关系接口回调
同一 Activity 内多个 Fragment 共享状态共享 ViewModel
页面观察持续变化的状态StateFlow / LiveData
组件之间进行松散的事件通知EventBus
跨进程通信Binder / AIDL

EventBus 的价值是降低发布者和订阅者之间的直接依赖。使用时要同时明确三件事:事件由谁发送、订阅者何时有效、处理逻辑运行在哪个线程。

掌握这三点,再配合正确的注册与注销,就能完成 Android 中常见的事件通信需求。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/6 3:46:43

光纤色序检测仪 毫秒节拍的轻量化实现

1. 生产痛点 在光通信布线、跳线生产和光缆组件装配环节&#xff0c;光纤&#xff08;或尾纤、跳线&#xff09;的色序是保证链路可维护性的基础约定。行业通用的 12 色序为&#xff1a;蓝、橙、绿、棕、灰、白、红、黑、黄、紫、粉、青&#xff0c;按松套管或束管内的位置依次…

作者头像 李华
网站建设 2026/11/6 3:46:20

火锅底料、辣椒酱的 “渣感“ 与 “分层“,根源往往不在细度

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/11/6 3:46:06

AI招聘智能体怎么用?从一个岗位到跑起来只要哪几步

AI招聘智能体怎么配、配置要花多久、上线之后要不要专人盯——这类问题在购后阶段出现得比想象中多。有企业把工具买回去两周&#xff0c;问的第一句是「配置在哪」&#xff0c;第二句是「配好了怎么知道有没有用」。这两句背后是同一件事&#xff1a;智能体不是装上就能干活的…

作者头像 李华
网站建设 2026/11/6 3:46:06

电离层扰动影响RTK精度的机理与工程应对方法

在工程测量、地质形变监测、水利设施安全监测等泛自然资源业务中&#xff0c;RTK技术承担着提供实时厘米级位置参考的任务。作业人员通常依赖网络RTK服务完成控制测量、像控点采集与位移监测&#xff0c;对固定解的稳定性与坐标成果的连续性有较高要求。但在实际工程中&#xf…

作者头像 李华
网站建设 2026/11/6 3:45:50

收到律师函那天,两小时都不够找一张图的授权

“这张图有授权吗&#xff1f;”——法务在群里问的这句话&#xff0c;看起来是个是非题&#xff0c;实际上能让整个部门翻两天文件夹。有客户跟我讲过一次经历&#xff1a;周五下午收到律师函&#xff0c;指着一张三年前的产品图。素材在哪个盘、当时谁对接、合同在哪台电脑上…

作者头像 李华