1、EventBus的基本使用
使用之前将EventBus添加到你的项目中,如下:
implementation 'org.greenrobot:eventbus:3.1.1'
第一步:定义事件
事件是常用Java bean对象,没有任何特定要求,如下:
package com.sample.retrofitdemo;
public class MessageEvent {
private String message;
public MessageEvent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
第二步:准备订阅者
订阅者实现事件处理方法(也称为“订阅者方法”),这些方法将在发布事件时调用。这些是使用@Subscribe注释定义的。
请注意,使用EventBus 3时,可以随便命名方法名称(没有像EventBus 2中那样的命名约定)。
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent me) {
tv.setText(me.getMessage());
}
用户也需要注册和注销EventBus。只有在订阅者注册时,他们才会收到活动。在Android中,在Activity和Fragment中,您通常应该根据其生命周期进行注册。对于大多数情况,onStart / onStop工作正常:
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
第三步:发布活动
从代码的任何部分发布事件。所有当前注册的与事件类型匹配的订阅者都将收到它。
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
2、线程模式
EventBus可以为您处理线程:事件可以发布在与发布线程不同的线程中。一个常见的用例是处理UI更改。在Android中,UI更改必须在UI(主)线程中完成。另一方面,网络或任何耗时的任务不得在主线程上运行。EventBus帮助您处理这些任务并与UI线程同步(无需深入研究线程转换,使用AsyncTask等)。
在EventBus中,您可以使用四个ThreadModes中的一个来定义将调用事件处理方法的线程。
ThreadMode:POSTING
如果订阅者在使用事件处理函数时候指定了这种线程模式,那么该事件是在哪个线程发布出来的,那么处理函数就会在这个线程中执行,也就是说发布事件和接收事件是在同一个线程。在这种线程模式下尽量避免做耗时操作,因为会阻塞事件的传递,很有可能引起ANR
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessageEvent(MessageEvent me) {
tv.setText(me.getMessage());
}
ThreadMode:MAIN
如果线程模式是MAIN,那么无论事件是在什么线程发送出来的,该事件处理函数都会在UI线程中执行,可以用来更新UI,但是注意不要做耗时操作。
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent me) {
tv.setText(me.getMessage());
}
ThreadMode: BACKGROUND
这种线程模式意思是,如果事件是在UI线程中发布出来的,那么该事件处理函数就会在子线程中执行;如果事件是在子线程中发布的,那就还在这个线程中处理。在这种模式下是不能更新UI操作的
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessageEvent(MessageEvent me) {
tv.setText(me.getMessage());
}
ThreadMode: ASYNC
这种线程模式就是说,无论事件在哪个线程发布,该事件的处理函数都会在新建的子线程中执行,此事件处理函数中是不能进行UI操作的
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessageEvent(MessageEvent me) {
tv.setText(me.getMessage());
}
3、EventBus的配置
EventBus提供了很多配置,一般的情况下我们可以不用配置.但是,如果你有一些其他要求,比如控制日志在开发的时候输出,发布的时候不输出,在开发的时候错误崩溃,而发布的时候不崩溃…等情况。
EventBus提供了一个默认的实现,但不是单例。
EventBus eventBus = new EventBus();
//下面这一条的效果是完全一样的
EventBus eventBus = EventBus.builder().build();
//修改默认实现的配置,记住,必须在第一次EventBus.getDefault()之前配置,且只能设置一次。建议在application.onCreate()调用
EventBus.builder().throwSubscriberException(BuildConfig.DEBUG).installDefaultEventBus();
4、粘性事件
EventBus除了普通事件也支持粘性事件,这个有点类似广播分类中的粘性广播。本身粘性广播用的就比较少,为了方便理解成订阅在发布事件之后,但同样可以收到事件。订阅/解除订阅和普通事件一样,但是处理订阅函数有所不同,需要注解中添加sticky = true
@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.getDefault().postSticky(new MessageEvent(“Hello everyone!”));
手动获取和删除粘性事件
如您所见,最后一个粘性事件在注册时会自动传递给匹配的订阅者。但有时手动检查粘性事件可能更方便。此外,可能需要删除(消耗)粘性事件,以便它们不再被传递。例:
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
}
方法 removeStickyEvent被重载:当你传入类时,它将返回先前持有的粘性事件。使用此变体,我们可以改进前面的示例:
MessageEvent stickyEvent = EventBus.getDefault().removeStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
// Now do something with it
}
5、优先事项和事件取消
虽然EventBus的大多数用例不需要优先级,也不需要取消事件,但在某些特殊情况下它们可能会派上用场。例如,如果应用程序位于前台,则事件可能会触发某些UI逻辑,但如果应用程序当前对用户不可见,则会有不同的反应。
订户优先权
您可以通过在注册期间为订户提供优先级来更改事件传递的顺序。
@Subscribe(priority = 1);
public void onEvent(MessageEvent event) {
...
}
在同一传递线程(ThreadMode)中,较高优先级的订户将在优先级较低的其他订户之前接收事件。默认优先级为0。
注意:优先级不会影响具有不同ThreadModes的订阅者的传递顺序!
取消活动发送
您可以通过从订阅者的事件处理方法调用cancelEventDelivery (Object 事件) 来取消事件传递过程 。任何进一步的活动交付将被取消,后续订阅者将不会收到该活动。
// Called in the same thread (default)
@Subscribe
public void onEvent(MessageEvent event){
// Process the event
...
// Prevent delivery to other subscribers
EventBus.getDefault().cancelEventDelivery(event) ;
}
事件通常由优先级较高的订户取消。取消仅限于在发布线程(运行事件处理方法 ThreadMode .PostThread)
6、订阅者索引
订户索引是EventBus 3的新功能。它是一种可选的优化,可加快初始订户注册。
可以使用EventBus批注处理器在构建期间创建订户索引。虽然不需要使用索引,但建议在Android上获得最佳性能。
指数先决条件
请注意,只能为订阅者和事件类公开索引@Subscriber方法。此外,由于Java注释处理本身的技术限制,@ Subscribe注释在匿名类中无法识别。
当EventBus无法使用索引时,它将在运行时自动回退到反射。因此它仍然可以工作,只是有点慢。
如何生成索引 使用annotationProcessor
如果您没有使用Android Gradle Plugin 2.2.0或更高版本,请使用android-apt配置。
要启用索引生成,您需要使用annotationProcessor 属性将EventBus批注处理器添加到构建中 。还要设置参数 eventBusIndex以指定要生成的索引的完全限定类。例如,将以下部分添加到Gradle构建脚本中:
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'
}
用kapt
如果要在Kotlin代码中使用EventBus,则需要使用 kapt 而不是 annotationProcessor :
apply plugin: 'kotlin-kapt' // ensure kapt plugin is applied
dependencies {
implementation 'org.greenrobot:eventbus:3.1.1'
kapt 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}
kapt {
arguments {
arg('eventBusIndex', 'com.example.myapp.MyEventBusIndex')
}
}
使用android-apt
如果上述方法不适合您,可以使用android-apt Gradle插件将EventBus注释处理器添加到您的构建中。将以下部分添加到Gradle构建脚本:
buildscript {
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
compile 'org.greenrobot:eventbus:3.1.1'
apt 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}
apt {
arguments {
eventBusIndex "com.example.myapp.MyEventBusIndex"
}
}
如何使用索引
成功构建项目后,将为您生成使用eventBusIndex指定的类 。然后在设置EventBus时将其传递给它:
EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();
或者,如果您想在整个应用中使用默认实例:
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
// Now the default instance uses the given index. Use it like this:
EventBus eventBus = EventBus.getDefault();
索引您的库
您可以将相同的原则应用于作为库的一部分的代码(而不是最终的应用程序)。这样,您可以拥有多个索引类,您可以在EventBus设置期间添加这些索引类,例如:
EventBus eventBus = EventBus.builder()
.addIndex(new MyEventBusAppIndex())
.addIndex(new MyEventBusLibIndex()).build();
最好实在Application启动的时候就调用这行代码保证之后所有的EventBus都默认使用了加速模式
7、EventBus的混淆
proguard-rules.pro文件中配置混淆
-keepattributes *Annotation*
-keepclassmembers class * {
@org.greenrobot.eventbus.Subscribe ;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
(java.lang.Throwable);
}
8、异步执行器(AsyncExecutor)
AsyncExecutor就像一个线程池,但有失败(异常)处理。失败是抛出异常,AsyncExecutor会将这些异常包装在一个自动发布的事件中。
免责声明:AsyncExecutor是一个非核心实用程序类。它可能会为后台线程中的错误处理节省一些代码,但它不是核心EventBus类。
通常,您调用 AsyncExecutor 。create ()创建一个实例并将其保留在Application范围内。然后执行一些操作,实现 RunnableEx接口并将其传递给AsyncExecutor的execute方法。与Runnable不同 , RunnableEx可能会抛出异常。
如果 RunnableEx实现抛出异常,它将被捕获并包装到ThrowableFailureEvent中,该ThrowableFailureEvent将被发布。
执行示例:
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
}
AsyncExecutor Builder
如果要自定义AsyncExecutor实例,请调用静态方法 AsyncExecutor 。builder ()。它将返回一个构建器,该构建器允许您自定义EventBus实例,线程池和失败事件的类。
另一个自定义选项是执行范围,它为失败事件提供上下文信息。例如,失败事件可能仅与特定的Activity实例或类相关。
如果您的自定义失败事件类实现HasExecutionScope接口,AsyncExecutor将自动设置执行范围。像这样,您的订阅者可以查询失败事件的执行范围并根据它做出反应。
参考文献:EventBus官网文档