这篇文章主要记录一下EventBus的使用,详情EventBus官方文档
1. 概念
EventBus能够简化各组件间的通信,让我们的代码书写变得简单,能有效的分离事件发送方和接收方(也就是解耦的意思),能避免复杂和容易出错的依赖性和生命周期问题。
-
事件(Event):又可称为消息,本文中统一用事件表示。其实就是一个POJO对象
事件分为一般事件和 Sticky 事件,相对于一般事件,Sticky 事件不同之处在于,当事件发布后,再有订阅者开始订阅该类型事件,依然能收到该类型事件最近一个 Sticky 事件。 -
订阅者(Subscriber):订阅某种事件类型的对象。当有发布者发布这类事件后,EventBus 会执行订阅者的 @Subscribe注解标记的方法,叫做事件响应。订阅者通过 register 接口订阅某个事件类型,unregister 接口退订。订阅者存在优先级,优先级高的订阅者可以取消事件继续向优先级低的订阅者分发(但是取消事件的线程必须和发布事件的线程一致),默认所有订阅者优先级都为 0。
-
发布者(Publisher):发布某事件的对象,通过 post 接口发布事件。
2. 简单使用
2.1. 添加EventBus库
api 'org.greenrobot:eventbus:3.1.1'
2.2. 新建一个POJO对象代表一个事件(Event)
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
2.3. 在生命周期里面注册和取消注册
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
2.4. 订阅者处理事件
// 当 MessageEvent 事件被发布这个方法会被调用(在主线程中显示一个 Toast)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
2.5. 发布事件
发布事件的类和订阅者的类不相同时,两个类不直接引用就可以进行通信.
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
3. 扩展
3.1. ThreadMode (线程模式)
订阅者响应事件的方法是通过 @Subscribe 注解标注的,其中有个 ThreadMode 属性可以进行控制响应事件方法的执行线程,一共有 5 种线程模式可以进行配置
-
ThreadMode: POSTING (默认)
订阅者(Subscriber) 在 发布者(Publisher) post 事件相同的线程中执行响应事件的方法,若 发布者(Publisher) post 的线程是主线程,则 订阅者(Subscriber) 处理事件的线程也在主线程中执行,若 发布者(Publisher) post 事件的线程是子线程,则 订阅者(Subscriber) 处理消息事件的线程也是在子线程中执行.
// Called in the same thread (default)
// ThreadMode is optional here
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessage(MessageEvent event) {
log(event.message);
}
-
ThreadMode: MAIN
订阅者(Subscriber) 在主线程中执行响应事件的方法,如果 发布者(Publisher) post 事件是主线程,则直接调用响应事件的方法,如果 post 的是子线程,则加入到主线程的消息循环队列中执行响应事件的方法,
// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) {
textField.setText(event.message);
}
-
ThreadMode: MAIN_ORDERED
订阅者(Subscriber) 在主线程中执行响应事件的方法 和 ThreadMode: MAIN 区别在于,不管 发布者(Publisher) post 事件是什么线程 ,MAIN_ORDERED会把事件加入到主线程的消息循环队列中执行,而不会直接调用处理消息的方法
// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
public void onMessage(MessageEvent event) {
textField.setText(event.message);
}
-
ThreadMode: BACKGROUND
订阅者(Subscriber) 在子线程中执行响应事件的方法.若 发布者(Publisher) post 事件为主线程,则在后台子线程中执行.所有的 ThreadMode: BACKGROUND 事件要转化在子线程处理的都共用一个相同的后台子线程 ,若 发布者(Publisher) post 事件的线程为子线程,则就直接在 post 事件的线程中处理.
// Called in the background thread
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessage(MessageEvent event){
saveToDisk(event.message);
}
-
ThreadMode: ASYNC
订阅者(Subscriber) 在独立的子线程中执行响应事件的方法,既不是 发布者(Publisher) post 事件的线程,不是主线程,也不是 ThreadMode: BACKGROUND 的后台子线程 .发布者(Publisher) post 事件的线程不会等待订阅者(Subscriber) 处理事件的线程响应.适用于处理事件时间较长的情况
// Called in a separate thread
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessage(MessageEvent event){
backend.send(event.message);
}
3.2. EventBus 配置
EventBus eventBus = EventBus.builder()
.logSubscriberExceptions(false) // 默认为 ture 是否记录 调用订阅者响应事件的方法出现异常时的异常日志
.logNoSubscriberMessages(false) // 默认为 ture 是否记录 发布者(Publisher) 发布事件时没有订阅者(Subscriber) 的日志
.sendNoSubscriberEvent(false) // 默认为 ture 当发布者(Publisher) 发布事件时没有订阅者(Subscriber) 是否将事件转化为 post 一个 NoSubscriberEvent事件
.sendSubscriberExceptionEvent(false) // 默认为 ture 调用订阅者响应事件的方法出现异常时 是否 post 一个SubscriberExceptionEvent 事件
.throwSubscriberException(BuildConfig.DEBUG) // 默认为 false 调用订阅者响应事件的方法出现异常时是否抛出 EventBusException
.eventInheritance(false) // 默认为 ture 若 发布者(Publisher) 发布的事件是订阅者(Subscriber) 的订阅事件的子类,是否将事件传递给订阅者(Subscriber) 处理
.ignoreGeneratedIndex(true) //默认为 false 是否忽略 Index
.strictMethodVerification(true) // 默认为 false ,是否严格认证 @Subscribe 注解标注的订阅者响应事件方法,如果 方法是0个或多于1个参数,或者是 非 public, 抽象的,静态的,会抛出 EventBusException
.installDefaultEventBus();
3.3. Sticky 事件
Sticky 事件不同之处在于,当事件发布后,再有订阅者开始订阅该类型事件,依然能收到该类型事件最近一个 Sticky 事件。
-
订阅Sticky事件
// UI updates must run on MainThread
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {
textField.setText(event.message);
}
-
发布Sticky事件
EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));
-
移除Sticky事件
Sticky事件 在事件发布之后依然会接收的到,所以不会丢失,若不需要时必须手动移除
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
}
移除 Sticky事件 还有一个重载的方法
MessageEvent stickyEvent = EventBus.getDefault().removeStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
// Now do something with it
}
3.4. 优先级和取消事件传递
-
优先级
默认优先级是 0,高优先级的的订阅者先接收到事件.只有相同在ThreadMode 下才能比较优先级 不同的 ThreadMode下的订阅者的优先级别不起作用
@Subscribe(priority = 1);
public void onEvent(MessageEvent event) {
...
}
-
取消事件传递
只有 和 发布者(Publisher) post 事件相同的线程的 订阅者(Subscriber) 才能取消事件传递,不然的话会报异常
/ Called in the same thread (default)
@Subscribe
public void onEvent(MessageEvent event){
// Process the event
...
// Prevent delivery to other subscribers
EventBus.getDefault().cancelEventDelivery(event) ;
}
3.5. Index(索引)
Index是 EventBus 3 上添加的新特性,默认是用使用反射,而 Index 是编译时 使用 annotationProcessor 生成辅助的 SubscriberInfoIndex 类 ,里面会记录订阅者信息,就不用反射扫描类中方法的,所以 Android上推荐使用 Index ,效率更高
-
使用 annotationProcessor 生成 Index
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [ eventBusIndex : 'com.example.myapp.MyEventBusIndex' ]
}
}
}
}
dependencies {
implementation 'org.greenrobot:eventbus:3.1.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}
-
使用 index
配置好 annotationProcessor 后使用 index 和之前的唯一区别是 EventBus.getDefault()之前必须使用 addIndex 方法进行初始化
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
// Now the default instance uses the given index. Use it like this:
EventBus eventBus = EventBus.getDefault();
3.6. AsyncExecutor 辅助类
AsyncExecutor是一个辅助类会创建一个线程池,在 RunnableEx 里面执行出现异常会被捕获不用自己处理,并被转化为一个 ThrowableFailureEvent事件并 post 出去
AsyncExecutor.create().execute(
new AsyncExecutor.RunnableEx() {
@Override
public void run() throws LoginException {
// No need to catch any Exception (here: LoginException)
remote.login();
EventBus.getDefault().postSticky(new LoggedInEvent());
}
}
);
@Subscribe(threadMode = ThreadMode.MAIN)
public void handleLoginEvent(LoggedInEvent event) {
// do something
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void handleFailureEvent(ThrowableFailureEvent event) {
// do something
}
网友评论