首页 / 资讯中心 / 文章详情

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

Android EventBus 入门:原理、使用与常见踩坑 ★ FEATURED ARTICLE
一、为什么需要 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) { findViewByIdTextView(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 等机制。七、什么时候适合使用 EventBusEventBus 适合组件之间关系比较松散、需要广播通知的场景例如多个模块监听某个业务操作完成。但它也有代价消息调用链变得不直观。查看一处post()通常无法直接看出所有接收方需要继续搜索订阅代码。因此选择通信方式时可以参考下面的思路场景可以优先考虑两个对象之间有明确调用关系接口回调同一 Activity 内多个 Fragment 共享状态共享 ViewModel页面观察持续变化的状态StateFlow / LiveData组件之间进行松散的事件通知EventBus跨进程通信Binder / AIDLEventBus 的价值是降低发布者和订阅者之间的直接依赖。使用时要同时明确三件事事件由谁发送、订阅者何时有效、处理逻辑运行在哪个线程。掌握这三点再配合正确的注册与注销就能完成 Android 中常见的事件通信需求。
阅读完成 · 觉得有帮助?
咨询建站