EventBus教程

在上一篇文章中,已经稍微了解了下EventBus和一些基本概念,包括如何在项目中添加EventBus支持。下面就深入学习如何使用。

一般使用和API

依据上篇中的三个步骤,并进行一些扩展。

1: 定义 EventBus

Events are POJO (plain old Java object) without any specific requirements.

事件就是一个包含特定要求的原始Java对象

1publicclassMessageEvent { 2 publicfinal String message; 3 4 publicMessageEvent(String message) { 5 this.message = message; 6 } 7}

2: 准备订阅者

订阅者实现事件处理方法onEvent,当一个事件被接收时会调用这个方法。 这些订阅者需要在总线中注册和解注册他们自己。

1@Override 2 publicvoid onStart() { 3 super.onStart(); 4 EventBus.getDefault().register(this); 5 } 6 7 @Override 8 publicvoid onStop() { 9 EventBus.getDefault().unregister(this); 10 super.onStop(); 11 } 12 13 // This method will be called when a MessageEvent is posted 当一个MessageEvent被传递的时候,这个方法会被调用publicvoid onEvent(MessageEvent event){ 14 Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show(); 15 } 16 17 // This method will be called when a SomeOtherEvent is posted 当一个SomeOtherEvent被传递的时候,这个方法会被调用publicvoid onEvent(SomeOtherEvent event){ 18 doSomethingWith(event); 19 }

3: 提交事件

从你代码的任意地方发布这个事件, 所有匹配这个事件的订阅者会接收到它。

EventBus.getDefault().post(new MessageEvent("Hello everyone!"));

传递线程和ThreadModes

EventBus可以帮助你处理线程问题: 事件可以在与提交线程不同的线程中被提交。 

一个常用的场景是处理UI改变。在Android中,UI 改变必须在UI线程中进行。另外,网络请求以及其他的耗时任务都不应该在主线程中操作。 EventBus可以帮助你处理这些事情,并与主线程同步(而不必使用AsynTask深入处理线程过渡等)。

在EventBus中,你可以使用ThreadMode定义线程,这个线程可以调用事件处理方法onEvent。

  • 提交线程:订阅者默认会在与提交事件相同的线程中被调用。事件传递意味着最小的开销,因为它完全避免了线程切换。所以,对于简单的短时任务,这是一种被推荐的模式,对主线程不会有什么要求。使用这种模式的事件处理器应该快速返回以避免阻塞提交线程, 因为这个线程可能是主线程。例如:

    1// Called in the same thread (default) 2 3public void onEvent(MessageEvent event) { 4 log(event.message); 5}
  • 主线程:订阅者会在Android的主线程中被调用(UI线程)。如果事件发布线程是主线程,那么,事件处理器方法会被直接调用。使用这种模式的事件处理器必须迅速返回以避免阻塞主线程。例如:

    1// Called in Android UI's main thread 2 3public void onEventMainThread(MessageEvent event) { 4 textField.setText(event.message); 5}
  • 后台线程:订阅者会在后台线程中被调用。如果提交线程不是主线程,那么事件处理方法会在提交线程中被直接调用。如果发布线程是主线程,则事件总线会使用一个能够传递所有事件的单独后台线程。使用这种模式的事件处理器应该快速返回,以免阻塞后台线程。

    1// Called in the background thread 2 3public void onEventBackgroundThread(MessageEvent event){ 4 saveToDisk(event.message); 5}
  • 同步:事件处理器方法在独立的线程中被调用。这个线程通常独立于提交线程和主线程。使用这种模式,发布事件始终不会等待事件处理器方法。如果他们的执行过程比较耗时的话,事件处理器方法应该使用这种模式。避免同时触发大量的耗时的异步处理方法以限制并发线程。 EventBus使用一个线程池从完成的异步事件处理通知中高效地复用线程。

    1// Called in a separate thread 2 3public void onEventAsync(MessageEvent event){ 4 backend.send(event.message); 5}

注:EventBus根据名字在合适的线程中调用onEvent方法(onEvent, onEventA)


订阅者优先级和有序事件传递

可以在注册订阅者的时候,提供一个优先级改变事件传递的顺序。

1 int priority =1; 2 EventBus.getDefault().register(this, priority);

在同一个传递线程中(ThreadMode),优先级高的订阅者会在优先级低的订阅者之前收到事件,默认优先级为0.

注意:优先级不会影响具有不同ThreadModes的订阅者之间的传递顺序

使用EventBusBuilder配置EventBus

EventBus2.3  添加了EventBusBuilder用于配置不同的EventBus切面。例如, 下面是创建一个在发布的事件没有订阅者的情况下保持静默的EventBus的过程。

EventBus eventBus = EventBus.builder().logNoSubscriberMessages(false).sendNoSubscriberEvent(false).build();

另一个是当订阅者执行失败时抛出异常的例子。注意:  EventBus捕获到从onEvent方法中抛出的异常,并发送一个SubscirberExceptionEvent,不过不一定要处理。

EventBus eventBus = EventBus.builder().throwSubscriberException(true).build();

可以查看EventBusBuilder类和其文档获取可能的配置。

配置默认的EventBus实例

使用EventBus.getDefualt()是一种获取共享EventBus实例的简单方式。EventBusBuilder也允许使用installDefaultEventBus()配置这个默认的实例。

例如,可以通过配置默认的事件总线方法实例重新抛出onEvent方法中发生的异常。但是, 这个情况只使用于DEBUG构建中,因为这可能导致app奔溃。

注意:只能在默认的事件总线第一次被使用之前进行一次。确保在你的应用中的持续行为。在使用之前,你的Appllication类是配置默认事件总线的好地方。

取消事件传递

可以从订阅者的事件处理方法中通过cancelEventDelivery(Object event)取消事件传递过程. 这样所有的进阶事件传递都会被取消,后续订阅者不会再收到事件。

事件通常是被优先级高的订阅者取消。对运行于提交线程 ThreadMode.PostThread .的事件处理方法来说,取消过程是比较严格的。

粘性事件

有些事件在提交之后携带着有益的信息。例如,可能是一个表明一些出初始化工作完成的事件信号。 或者,如果你有一些传感器和位置信息,并且你希望持有为最近的值。 这样,你可以使用粘性事件,而不是实现自己的缓存。事件总线在内存中保持着特定类型的最终粘性事件。这些粘性事件可以被传递给订阅者或者用于明确查询。所以,不需要任何的特殊逻辑来考虑已经可以获得的数据。

粘性事件在一个特定事件之前发布。

EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));

此后,一个新的Act启动,使用registerSticky注册期间,就会立即获得之前发布的粘性事件。

1@Override 2 publicvoid onStart() { 3 super.onStart(); 4 EventBus.getDefault().registerSticky(this); 5 } 6 7 publicvoid onEventMainThread(MessageEvent event) { 8 textField.setText(event.message); 9 } 10 11 @Override 12 publicvoid onStop() { 13 EventBus.getDefault().unregister(this); 14 super.onStop(); 15 }

你也可以通过下面的方式,获得特定类型的最后一个粘性事件

       EventBus.getDefault().getStickyEvent(Class<?>  eventType)

也可以通过removeStickyEvent()方法移除之前提交的粘性事件。 这个方法具有一个固定的事件对象或者是一个事件类。 像这样,可以创建可以消费的事件。 记住,一个事件类型,只有最后的事件会被保持。

混淆配置

混淆器,会混淆方法名,不过,onEvent()方法不会被重命名,因为他们是通过反射获得的。在混淆器中使用下面的代码。

1-keepclassmembers class ** { 2 public void onEvent*(**); 3} 4 5# Only required if you use AsyncExecutor 6-keepclassmembers class * extends de.greenrobot.event.util.ThrowableFailureEvent { 7 <init>(java.lang.Throwable); 8}

异步执行器

申明:AsyncExecutor是一个非核心的实体类。这个类可以通过后台错误处理线程省下一些代码,但不是EventBus的核心。

AsyncExecutor就像线程池,只不过具有失败处理能力。如果失败会抛出异常,这个异常被包装在一个事件内部中,并自动被AsyncExecutor抛出。

通常情况下,可以使用AsyncExecutor.create()创建一个实例,并在Application作用域内持有。如果要执行一些事情,可以实现RunnableEx接口,并将其传递到AsyncExecutor的执行方法中。与Runnable不同,RunnableEx可能抛出异常。

如果RunnableEx的实现抛出异常,这个异常会被捕获到,并包装进一个ThrowableFailureEvent对象中,发布出来。

Code example for execution:

1AsyncExecutor.create().execute( 2 new RunnableEx { 3 publicvoid run throws LoginException { 4 remote.login(); 5 EventBus.getDefault().postSticky(new LoggedInEvent()); 6 // No need to catch Exception 7 } 8 } 9}

Code example for the receiving part:

1publicvoid onEventMainThread(LoggedInEvent event) { 2 // Change some UI 3} 4 5publicvoid onEventMainThread(ThrowableFailureEvent event) { 6 // Show error in UI 7}

AsyncExecutor 构建器

如果你想自定义AsyncExecutor实例,可以调用静态方法AsyncExecutor.build()方法。这样可以返回一个允许你自定义EventBus实例,线程池,以及失败事件类的构造器。

另一个自定义选项是执行作用域,这个选项会给出失败事件的上下文信息。例如,一个失败的事件可能只与一个特定的Activity实例或者类有关。如果你自定义失败事件类实现HasExecutionScope接口,则AsyncExecutor会自动设置执行作用域。比如,订阅者可以在执行作用域内查询失败事件并与之交互。


与Otto比较

Check the COMPARISON.md

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )