异步执行器(AsyncExecutor)
异步执行器就像是线程池,但是包含了失败后的异常处理。失败时会抛出异常,这些异常会被异步执行器捕捉并且包装到事件里面,最后把事件发送出去。
申明:AsyncExecutor
是一个非核心工具类。它可以帮你减少一些在后台线程上发生的异常处理,但是它不是核心的EventBus类。
通常,你通过调用AsyncExecutor.create()
来创建一个实例并且在Application作用域保存。之后,如果需要执行某个操作,只需实现RunnableEx接口并且传入AsyncExector的execute方法。与Runnable
不同的是,RunnableEx
可能会抛出异常。
如果RunnableEx
抛出了异常,这个异常会被AsyncExecutor捕捉并且包装进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
}
异步执行器Builder(AsyncExecutor Builder)
如果想自定义AsyncExecutor实例,你可以调用静态方法AsyncExecutor.builder()
。它会返回一个builder,允许你自定义EventBus实例、线程池、失败事件的类。
另一个自定义选项是运行范围(execution scope),这会提供失败事件的上下文信息。举例来说,一个失败事件只会关联到一个特定的Activity实例或类。
如果你的自定义失败事件类实现了HasExecutionScope
接口,AsyncExecutor会自动设置运行环境上下文。这样一来,你的订阅者就能查到失败事件的上下文,然后根据上下文进行相应的处理。
EventBus常见问题和异常解决
常见问题
EventBus是一种软件架构吗?
不,它本身不是。EventBus可以帮助解决一些架构问题,比如组件之间的解耦。但你仍然需要自己设计想要的app架构。大多数时候,会把你的app分成很多层(比如,UI层,逻辑层,数据获取层),这有助于分解和解决app的复杂性问题。根据开发者的不同喜好,EventBus可以出现在任何你想要的分层(EventBus关心的是切片,译者注:不了解切片的,可以搜索AOP),也可以严格地只在同一层传递(但这可能会需要多次重复发送事件,这是十分不便的)。始终记得:不要过度使用EventBus,你可以直接调用方法。
EventBus与Android的广播机制有什么不同
与Android的广播机制不同,EventBus使用标准Java类作为事件并且提供了更多便捷的API。EventBus会有更多的使用场景,而你也不再需要经历繁杂的Intent设置,填充Intent的Extras,实现广播接收者然后再把数据从Intent Extras中提取出来。同时,使用EventBus传递事件开销要小得多。
异常解决
当我发送一个事件时,会接收到两次或多次事件。但是我只有一个订阅方法。为什么会导致这个问题?
检查你是否有多个订阅者类的实例被同时注册到了EventBus。典型的场景有:1)忘记调用unregister;2)某个Fragment多次attach到Activity。想要验证这些场景,只要在你注册和反注册的地方添加log:如果注册调用的次数大于反注册次数+1,就可以证明你的app没有正确的进行注册和反注册。
注册一个订阅者时抛出了java.lang.NoClassDefFoundError,我该怎么办?
首先介绍一些背景知识来帮助你理解为什么会发生这种情况:一些Android版本在调用getDeclaredMethods
和getMethods
时似乎存在bug。当类中的某个方法包含了当前API level上不存在的类时会抛出异常。比如,PersistableBundle这个类是在API level 21时添加的。随之一起的,在Activity中添加了一些新的生命周期方法,其中就有使用PersistableBundle作为参数的方法,比如onCreate (Bundle savedInstanceState, PersistableBundle persistentState)。如果你覆写了这个方法,然后把它注册到EventBus,那么在旧版本的设备上,你就会引发之前描述的bug。知道了原理,就能轻松的解决这个问题了。
下面是关于修复这个问题的一些建议(按照顺序逐条检查):
- 也许你只是意外的覆写了带有PersistableBundle参数的生命周期方法。如果是这样,就换个不带PersistableBundle参数的方法,比如onCreate (Bundle savedInstanceState)。
- 使用EventBus 3的订阅者索引。这可以避免反射,同样也避免了这个问题。甚至你还能因此获得一点好处,使得app启动时注册订阅者的速度大大增加。
- 从订阅者类中移除这些有问题的方法。把这些订阅方法移动到新的订阅者类中,或者把有问题的方法移到非订阅者类都可以。
- 如果有问题的方法是public的,将它改成非public。因为“B方案”的存在,这样也能避免bug:EventBus会先调用getDeclaredMethods,这会导致失败。接下来(译者注:捕捉到之前的失败后),EventBus会再次尝试使用getMethods(“B方案”)。后者会成功,因为后者只返回public方法。然而,这是从性能上来考虑最差的解决方案(2次反射调用,而不是1次,并且还有getMethods方法会将整个类的继承结构都考虑进去)。
为什么我会得到警告:“The following options were not recognized by any processor: ‘[eventBusIndex]’”?
这会发生在你设置好了build脚本,但是还没有在代码中添加任何的注解(译者注:当然是指EventBus自己的注解)。因为没有任何的@Subscribe注解,注解处理器也就不会被执行,所以也就无法去使用’eventBusIndex’选项了。所以,只要加上一些@Subscribe注解就不会有这个问题了。
译者的一些额外说明
- 订阅方法,也就是想要加上@Subscribe注解的方法,无论你是否启用订阅者索引,都需要将该方法声明成public。