【Android】EventBus事件总线用法浅析
EventBus 是一个基于发布-订阅模式的 Android 库,专注于简化组件之间的通信。提供了一种高效、灵活且解耦的方式,用于在应用程序的不同组件(如 Activity、Fragment、Service 等)之间传递消息或事件。比如请求网络,两个Fragment之间需要通过Listener通信,这些需求都可以通过EventBus实现。
EventBus 的核心功能
- 解耦组件:
发布者和订阅者之间不需要直接引用,降低了耦合性。 - 跨线程通信:
可以指定事件处理在哪个线程执行(主线程、后台线程等)。 - 事件优先级:
支持设置订阅者的优先级,以决定事件处理的先后顺序。
EventBus使用方法
首先来看看EventBus的三要素!
1. Event(事件)
定义:事件是消息传递的载体,可以是任意类型的对象,例如自定义的类、基本数据类型的包装类等。
作用:用来承载需要传递的数据。
使用方式:开发者可以根据需求自定义事件类,携带需要传递的信息。
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
Subscriber(订阅者)
定义:订阅者是接收事件的对象。在 EventBus 中,订阅者需要通过注解
@Subscribe
标记特定的方法来订阅事件。注意:EventBus 3.0 之前,订阅的方法名称必须是固定的(如
onEvent
、onEventMainThread
等),而 3.0 之后方法名可以自定义,只需用@Subscribe
注解即可。订阅者需要指定线程模型,默认是
POSTING
模式。
Publisher(发布者)
定义:事件发布者是负责发送事件的组件,可以是应用中的任意部分。
作用:将事件发送到事件总线,由总线分发给匹配的订阅者。
使用方式:通过调用
EventBus.getDefault().post(Object)
发送事件。
EventBus.getDefault().post(new MessageEvent("Hello EventBus!"));
再来看看 EventBus 的 4 种 ThreadMode 线程模型!
POSTING(默认):如果使用事件处理函数指定了线程模型为POSTING,那么该事件是在哪个线程发布出来的,事件处理函数就会在哪个线程中运行,也就是说发送事件和接收事件在同一个线程中。在线程模型为POSTING的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR(Application Not Responding)问题。
MAIN:事件的处理会在切线程中执行。事件处理的时间不能太长,长了会引起ANR问题。
BACKGROUND:如果事件是在U线程中发布出来的,那么该事件处理函数就会在新的线程中运行;如果事件本来就是在子线程中发布出来的,那么该事件处理函数直接在发送事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。
ASYNC:无论事件在哪个线程中发布,该事件处理函数都会在新建的子线程中执行,同样,此事件处理函数中禁止进行UI更新操作。
EventBus 基本用法
EventBus 使用起来分为以下 5 个步骤:
(1)自定义一个事件类
public class MessageEvent {
...
}
(2)在需要订阅事件的地方注册事件
EventBus.getDefault().register(this);
(3)发送事件
EventBus.getDefault().post(messageEvent);
(4)处理事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void XXX(MessageEvent messageEvent) {
...
}
(5)取消事件订阅
EventBus.getDefault().unregister(this);
EventBus 的黏性事件
黏性事件(Sticky Event) 是 EventBus 提供的一种特殊事件传递机制,即在事件发送之后,即使没有订阅该事件,稍后订阅者也能接收到这个事件。主要用于处理订阅发生在事件发布之后的场景。
黏性事件的原理
当使用 postSticky()
方法发布事件时,EventBus 会将该事件保存到内部的缓存中(StickyEvent Map)。当订阅者订阅时,会立即检查是否有符合条件的黏性事件,如果有则立即分发给订阅者。这种机制特别适合需要保存全局状态或后续动态注册的场景。
详细实现步骤
1. 定义事件类
public class MessageEvent {
private String message;
public MessageEvent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
2. 订阅黏性事件
在 MainActivity
中定义一个处理黏性事件的方法,并用 @Subscribe
注解标记,同时指定 sticky = true
:
@Subscribe(threadMode = ThreadMode.POSTING, sticky = true)
public void onStickyEvent(MessageEvent messageEvent) {
tv_message.setText(messageEvent.getMessage());
}
threadMode = ThreadMode.POSTING
:订阅者运行在事件发布的线程。
sticky = true
:表示该方法订阅的是黏性事件。
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this); // 注册订阅者
}
@Override
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this); // 注销订阅者
}
3. 发布黏性事件
在 SecondActivity
中,通过 postSticky()
方法发布黏性事件:
bt_subscription.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().postSticky(new MessageEvent("黏性事件"));
finish(); // 返回到 MainActivity
}
});
在 MainActivity
中不要立即注册订阅者(不点击“注册事件”按钮)。
跳转到 SecondActivity
并点击“发送黏性事件”按钮,这会发布一个黏性事件并返回到 MainActivity
。
回到 MainActivity
,点击“注册事件”按钮,这时订阅方法会接收到之前发送的黏性事件,更新界面内容。
EventBus使用示例
你是西安邮电大学的一名正在学习安卓的带学生,此时你需要开发一个应用,其中包含如下内容:
- 登录页面: 用户输入账号和密码,点击登录。
- 首页: 显示用户的个性化内容,如推荐文章、好友动态等。
- 个人中心: 显示用户的昵称、头像和其他信息。
当用户成功登录后,首页和个人中心需要同时刷新数据以展示用户的最新状态。例如,首页需要显示用户的推荐内容,个人中心需要更新用户的昵称和头像。
常规实现的问题
- 接口直接调用: 登录成功后,直接调用首页和个人中心的刷新接口。这种方法需要登录页面直接依赖首页和个人中心的逻辑,导致强耦合。
- 广播机制: Android 的广播可以用来解决,但实现过程较复杂,还需要注册和过滤广播。
用 EventBus 实现
- 解耦: 登录页面不需要知道首页和个人中心的具体实现,只需发布一个登录成功的事件。
- 异步处理: 首页和个人中心可以根据需要选择在主线程或后台线程中处理事件。
实现步骤
1. 定义事件类
创建一个表示用户登录成功的事件:
public class LoginSuccessEvent {
private final String userId;
public LoginSuccessEvent(String userId) {
this.userId = userId;
}
public String getUserId() {
return userId;
}
}
2. 发布事件
在登录成功的逻辑中,发布事件通知其他组件:
// 假设登录成功后获取到用户ID
String userId = "12345";
EventBus.getDefault().post(new LoginSuccessEvent(userId));
3. 订阅事件
在需要响应登录成功事件的页面(如首页和个人中心),订阅事件:
首页代码:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onLoginSuccess(LoginSuccessEvent event) {
// 刷新推荐内容
refreshContent(event.getUserId());
}
@Override
protected void onStart(