Android中EventBus简单使用
Android中EventBus简单使用
EventBus是Android开发中常用的事件总线框架,用于处理跨页面、跨组件、跨线程的消息传递。本文将详细介绍EventBus的基本概念、使用方法及拓展功能,帮助开发者更好地理解和使用这一工具。
综述
消息总线(事件总线)被广泛应用于各类项目中。在Android开发中,随着项目规模的扩大,常常需要在不同页面、组件、线程甚至进程之间传递消息与数据。为了解决这一需求,工程师们发展了消息总线体系。目前主流的消息总线框架包括:
- Android原生的Broadcast和BroadcastReceiver体系
- GreenRobot公司出品的EventBus
- 基于RxJava的RxBus
- Android Jetpack库中的LiveDataBus
- 基于SharedFlow的FlowBus
EventBus处于一个承上启下的位置:它简化了组件间的通信,提高了开发效率,同时由于其流行时间较早,被广泛应用于大量早期Android项目中。虽然功能上不如RxBus强大,但这也降低了上手难度。其主要缺点是大规模使用时缺乏统一的管理和配置中心,导致在大型和超大型项目中难以维护。
概念
以下是GreenRobot官方给出的EventBus消息传递示意图:
此图描述使用EventBus传递一次消息所涉及的四个角色:
- 发布者(Publisher):负责发送消息
- 订阅者(Subscriber):负责接收消息
- 事件(Event):存放信息的事件对象
- EventBus:中转站,事件从发布者通过EventBus发送到订阅者那里
在使用时,开发者需要自己实现前三个角色,而中转站本身EventBus库已经自行实现。
使用方法
添加依赖
使用前,需要引入相关的库。在Gradle中加入:
implementation("org.greenrobot:eventbus:3.3.1")
// 或者是Java版本
implementation("org.greenrobot:eventbus-java:3.3.1")
如果使用的是Maven,则在依赖中加入:
<dependency>
<groupId>org.greenrobot</groupId>
<artifactId>eventbus-java</artifactId>
<version>3.3.1</version>
</dependency>
简单使用
首先需要定义一个事件Event,这个类一般单独定义:
// Java版本
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
然后定义一个订阅者,这个方法一般会放在需要接受信息的类中:
// Java版本
// This method will be called when a MessageEvent is posted (in the UI thread for Toast)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
// This method will be called when a SomeOtherEvent is posted
@Subscribe
public void handleSomethingElse(SomeOtherEvent event) {
doSomethingWith(event);
}
同时,定义的订阅者方法的类需要被注册才能被EventBus知晓:
// Java版本
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
最后,需要定义一个发布者:
// Java版本
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
拓展功能介绍
订阅方式
在实现订阅者的时候除了默认的使用@Subscriber
注解指定订阅者方法的做法之外,还可以使用拓展功能同时指定消息传输的线程:
// 默认方式
@Subscribe
public void handleSomethingElse(SomeOtherEvent event) {
doSomethingWith(event);
}
// 拓展方式
// Called in the same thread (default)
// ThreadMode is optional here
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessage(MessageEvent event) {
log(event.message);
}
// Called in a separate thread
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessage(MessageEvent event){
backend.send(event.message);
}
// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) {
textField.setText(event.message);
}
// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
public void onMessage(MessageEvent event) {
textField.setText(event.message);
}
// Called in the background thread
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessage(MessageEvent event){
saveToDisk(event.message);
}
- 使用
@Subscribe(threadMode = ThreadMode.POSTING)
等同于使用@Subscribe
,或者说后者就是前者的简写。 - 使用
@Subscribe(threadMode = ThreadMode.MAIN)
表示订阅者方法将在Android的主线程即UI线程中被调用,一般用来处理和UI有关的操作。 - 使用
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
将使得事件会严格的按照调用的顺序通过UI线程排队发送给订阅者。 - 使用
@Subscribe(threadMode = ThreadMode.BACKGROUND)
表示如果发布者不是在主线程发布的事件则直接发送给订阅者,如果发布方法在主线程发布的事件,EventBus将使用后台线程单独发送事件。 - 使用
@Subscribe(threadMode = ThreadMode.ASYNC)
表示EventBus将始终使用另外的单独线程将事件发送给订阅者方法,无论发布者是在哪个线程发送的事件。
粘性事件
粘性事件是一种特殊的事件,常规来说,一般发布者发送出去的事件如果发送后订阅者才注册,那么新注册的订阅者是不会收到发送者发送的数据。但是如果处于特殊场景下,项目需要订阅者在注册后接收注册前已经发出的信息,就可以使用粘性事件。
考虑以下假想场景:
一个项目需要和远程服务器进行连接并且隔一段时间动态获取数据并更新在Activity界面,所以使用了Service进行持续的自动运行。由于数据更新在UI上,所以设置为Activity处于onResume()
时开启Service,Activity处于onPause()
时停止Service。因为有时候用户会需要立刻刷新数据而不是等几十秒再刷新,所以设置使用EventBus通知Service终止当前的循环处理,重新且立刻开始间隔一段时候就访问一次服务器数据的流程。同时还存在另一个功能需求,每次调用均需要动态申请系统权限并获取用户的许可。
好,假设现在需要在获取用户许可后立刻通知服务器刷新。那么就会存在获取用户许可时因为调用的是系统窗口,Activity处于onStop()
回调周期,Service停止。用户点击确定后,Service可能已经开启也可能尚未重新初始化并注册订阅者。
所以,此时通过权限申请回调运行发布者的指令会时不时无效。而在以上场景中,使用粘性事件会是一种比较好的解决方案。
首先还是需要定义一个事件:
// 注意,事件命名在语法上是自由的,但是为了可读性,一般会从XXXEvent改为StickyXXXEvent或者XXXStickyEvent
public class StickyMessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
定义一个发布者:
// 定义发布者
EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));
最后定义一个接收者:
// 定义接收者
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
// UI updates must run on MainThread
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {
textField.setText(event.message);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
额外的,因为粘性事件会长期存放在EventBus中,一般在接收之后需要将其清除:
// 一般放在onEvent()中获取事件并处理后再执行
MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
// "Consume" the sticky event
EventBus.getDefault().removeStickyEvent(stickyEvent);
// Now do something with it
}
总结
以上就是EventBus的用法的简单整理,一般性的使用差不多只需要这些功能。其它的像是property、configuration、取消发送之类的功能一般不会用到。
同时需要额外注意,使用3.0之前的EventBus版本时,在app打包时,对EventBus相关方法进行混淆代码可能导致EventBus无法正常工作,需要在混淆中单独设置规则以避开。而3.0之后(包括3.0)的EventBus已经采用了新机制,不需要对此进行额外的处理。