介绍
EventBus是适用于Android和Java的发布/订阅事件总线。可简化活动,片段,线程,服务等之间的通信。代码更少,质量更高。
基本使用
- 项目配置EventBus依赖
implementation 'org.greenrobot:eventbus:3.2.0'
- 定义事件
public class MessageEvent { }
- 注册/解除订阅者和定义接受事件的方法,在Android中应该根据组件生命周期进行注册/解注册
@Override
public void onStart() {
super.onStart();
// 注册订阅者
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
// 解除订阅者
EventBus.getDefault().unregister(this);
}
// 定义接受事件的方法(可以指定线程模式、事件优先级、是否是粘性事件)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event){};
- 发布事件
EventBus.getDefault().post(new MessageEvent());
- 配置混淆(如果项目开启了混淆,则需要添加EventBus的混淆规则)
-keepattributes *Annotation*
-keepclassmembers class * {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# And if you use AsyncExecutor:
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
自定义配置
使用 EventBusBuilder 类来配置 EventBus 的方方面面。进入 EventBus 类中,可以看到 EventBusBuilder 会给 EventBus 赋值几个默认为 true 的属性配置值。
boolean logSubscriberExceptions = true;// 打印订阅异常日志
boolean logNoSubscriberMessages = true;// 打印没有订阅者的消息
boolean sendSubscriberExceptionEvent = true;// 订阅方法异常时发送SubscriberExceptionEvent事件
boolean sendNoSubscriberEvent = true;// 发送的消息没有订阅者时发送 NoSubscriberEvent 事件
boolean eventInheritance = true;// 事件允许继承,设置true意思是去查找该事件父类的订阅者,执行调用
boolean throwSubscriberException;// 回调订阅方法失败时,是否抛出异常
boolean ignoreGeneratedIndex;//是否强制使用发射查找订阅方法,忽略注解处理器生成的订阅信息Index
全局 app 中我们一般是使用 EventBus.getDefault()
这样的单例,那么如何将我们想要更改后的配置应用于单例中,从而仍然使用 EventBus.getDefault()
这样的简便写法?
EventBus.builder()
.logSubscriberExceptions(false)
.logNoSubscriberMessages(false)
.sendSubscriberExceptionEvent(false)
.sendNoSubscriberEvent(false)
.throwSubscriberException(false)
.installDefaultEventBus();
线程模式
- ThreadMode.POSTING
订阅者的事件处理程序方法将在发布事件的同一线程中被调用。这是默认值。事件传递是同步完成的,发布完成后将调用所有订阅者。此ThreadMode意味着开销最少,因为它避免了线程的完全切换。因此,这是已知的简单任务的推荐模式,该任务在很短的时间内即可完成,而无需主线程。使用此模式的事件处理程序应快速返回,以避免阻塞可能是主线程的发布线程。
-
ThreadMode.MAIN
订阅者的事件处理程序方法将在Android的主线程(有时称为UI线程)中调用。如果发布线程是主线程,则将直接调用事件处理程序方法(与ThreadMode.POSTING所述的同步)。使用此模式的事件处理程序必须快速返回以避免阻塞主线程。例:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) {
textField.setText(event.message);
}
-
ThreadMode.MAIN_ORDERED
订阅者的事件处理程序方法将在Android的主线程中调用。该事件始终排入队列,以便以后交付给订户,因此投递呼叫将立即返回。这使事件处理具有更严格和更一致的顺序(因此名称为MAIN_ORDERED)。例如,如果您在具有MAIN线程模式的事件处理程序中发布另一个事件,则第二个事件处理程序将在第一个事件处理程序之前完成(因为它被同步调用–将它与方法调用进行比较)。使用MAIN_ORDERED,第一个事件处理程序将完成,然后第二个事件处理程序将在稍后的时间点调用(一旦主线程具有容量)。使用此模式的事件处理程序必须快速返回以避免阻塞主线程。例:
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
public void onMessage(MessageEvent event) {
textField.setText(event.message);
}
-
ThreadMode.BACKGROUND
订阅者的事件处理程序方法将在后台线程中被调用。如果发布线程不是主线程,则将在发布线程中直接调用事件处理程序方法。如果发布线程是主线程,则EventBus使用单个后台线程,该线程将顺序传递其所有事件。使用此模式的事件处理程序应尝试快速返回以避免阻塞后台线程。例:
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessage(MessageEvent event){
saveToDisk(event.message);
}
- ThreadMode.ASYNC
订阅者的事件处理程序方法将在单独的线程中调用。这始终独立于发布线程和主线程。发布事件永远不会等待使用此模式的事件处理程序方法。如果事件处理程序方法的执行可能需要一些时间(例如,用于网络访问),则应使用此模式。避免同时触发大量长时间运行的异步处理程序方法,以限制并发线程数。EventBus使用线程池有效地重用已完成的异步事件处理程序通知中的线程。例:
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessage(MessageEvent event){
backend.send(event.message);
}
事件优先级/取消事件的分发
-
事件的优先级
@Subscribe中定义事件的优先级,看看@Subscribe中定义的属性
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Subscribe { ThreadMode threadMode() default ThreadMode.POSTING;//线程模式 boolean sticky() default false;//是否是粘性事件,默认false int priority() default 0;// 事件优先级,默认0 }
默认的优先级为 0,对于同一个事件,优先级高的订阅者将比优先级低的先接收到事件。值得注意的是,条件是在同一个线程中(threadMode 一致),在不同的 threadMode 值的订阅者之间的接受顺序并不会有影响。
-
取消事件的分发
EventBus 中提供 cancelEventDelivery(Object event) 来取消事件的进一步分发,则接下来的订阅者将不会收到事件。一般的情景都是用于发送事件,然后在优先级高的订阅者中处理完自己逻辑,之后不然让事件继续分发给其他的订阅者,则取消事件。需要注意的是取消操作,被限制在只能使用 threadMode 为 ThreadMode.POSTING 中。 -
粘性事件(StickyEvent)
一般的事件发布之后,当前所有已注册该事件类型的订阅者都将收到该事件。这个很容易理解,EventBus 遍历那些已经注册过的订阅者,然后调用订阅者的接受事件的方法。而sticky 事件在发布之后,仍然允许后来注册的订阅者响应函数去响应对应类型的 sticky 事件,sticky 事件在事件发布后,如果后来的订阅者再去订阅该类型事件,仍然可以收到该类型最近的一个 sticky 类型,因为它已经被预先存储在内存之中了。- 发送粘性事件
- postSticky(Object event)
- 获取粘性事件
- getStickyEvent(Class eventType)
- 移除粘性事件
- removeStickyEvent(Class eventType)
- removeStickyEvent(Object event)
- removeAllStickyEvents()
- 发送粘性事件
使用优化
-
订阅者索引
Subscriber Index 是EventBus3的新特性,是用于加速初始化订阅者注册的一个优化。使用订阅者索引避免了运行时使用反射进行订阅方法的昂贵查找。相反,EventBus注释处理器会在构建时进行查找。Subscriber Index 在编译期间通过 EventBus annotation processor(注解处理器) 被创建起来的。这个特性并不是必须使用的,但是是被推荐的,因为通过这个可以更快速度获得订阅者,从而去触发响应事件。 -
使用条件
只有被 @Subscribe 注解的方法,且订阅者类和事件类都必须为 public 才可以被建立索引。同时,由于 java 注解技术限制,@Subscribe 在匿名内部类中是不会被识别的。当 EventBus 不能使用索引时,它将自动回退到在运行时进行反射查找订阅方法,因此 EventBus 仍然能够工作,只是稍微慢一点而已的。推荐去使用 Subscriber Index 在编译期间建立了订阅者索引,所以更快获得订阅者,以调用它的响应函数。而如果不设置 Subscriber Index 其实也能使 EventBus 工作的很好,虽然获得订阅者时通过反射是慢了一点点而已,但仍然会帮助将订阅者缓存起来。 -
生成索引
// app build.gradle文件夹下配置
// Java:using annotationProcessor
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [ eventBusIndex : 'com.example.myapp.MyEventBusIndex' ]
}
}
}
}
dependencies {
def eventbus_version = '3.2.0'
implementation "org.greenrobot:eventbus:$eventbus_version"
annotationProcessor "org.greenrobot:eventbus-annotation-processor:$eventbus_version"
}
=========================================================================
// Kotlin: using kapt
apply plugin: 'kotlin-kapt' // ensure kapt plugin is applied
dependencies {
def eventbus_version = '3.2.0'
implementation "org.greenrobot:eventbus:$eventbus_version"
kapt "org.greenrobot:eventbus-annotation-processor:$eventbus_version"
}
kapt {
arguments {
arg('eventBusIndex', 'com.example.myapp.MyEventBusIndex')
}
}
- 使用索引
// 至少build一次项目,为了让注解处理程序生成Index类文件
// 第一种方式注入Index,然后直接构建出EventBus实例,这个全局使用即可。
EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();
// 第二种方式注入Index,之后还是通过EventBus.getDefault()获取,且只需要调用一次。
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
EventBus eventBus = EventBus.getDefault();
// 如果依赖库中需要支持订阅者,同样的配置原理生成Index,然后将其添加到EventBus中
EventBus eventBus = EventBus.builder()
.addIndex(new MyEventBusAppIndex())
.addIndex(new MyEventBusLibIndex()).build();