通过上一篇文章《EventBus 3.0相见恨晚》对EventBus3.0的原理及使用方法有了简单了解。
下面就其原理和使用方法做更深入细致的了解。
EventBus设计模式##
EventBus is an open-source library for Android using the publisher/subscriber pattern for loose coupling
这句话是greenrobot官网对EventBus的解释。
EventBus是一个针对Android为了松耦合的基于事件发布/订阅模式(观察者模式)的开源库。
从上图我们可以看到EventBus的设计结构非常简单。
EventBus 使用方式##
订阅者方法详细用法###
从之前对EventBus的Demo我们可以看到,使用EventBus的关键是订阅者方法的实现,也就是事件处理方法onMessageEvent的实现。这个方法,才是我们处理数据、实现UI更新的关键。
@Subscribe(threadMode = ThreadMode.POSTING,sticky = false,priority = 1)
public void onMessageEvent(MessageEvent event) {
tv.setText(event.message);
}
这里再强调一遍,这个订阅者方法一定要添加@Subscribe这个注解,这个注解的完整参数如上,后面是一些可选的参数。下面就各个参数做一下分析:
EventBus的ThreadMode总共有四种,并且都是在订阅者中的@Subscribe里进行制定的。下面就来看一下这四种ThreadMode。
四种threadMode模式####
- ThreadMode: POSTING
这时候订阅者执行的线程与事件的发布者所在的线程为同一个线程。也就是说事件由哪个线程发布的,订阅者就在哪个线程中执行。这个也是EventBus默认的线程模式;由于没有线程的切换,也就意味消耗的资源也是最小的。如果一个任务不需要多线程的,也是推荐使用这种ThreadMode的。
我们之前的Demo就是这样,事件发送是在SecondActivity的主线程,那么onMessageEvent在默认情况下必然会在主线程执行。
- ThreadMode: MAIN
从它的名字就很容易可以看出,他是在Android的主线程中运行的。如果提交的线程也是主线程,那么他就和ThreadMode.POSTING一样了。当然在这里由于是在主线程中运行的,所以在这里就不能执行一些耗时的任务。
还是之前的Demo,我们在SecondActivity中实现登录操作,正常情况下这必然是个网络请求,而这个网络请求必然不会在主线程(UI线程)中发生,所以,当我们完成网络请求发布事件的时候,发布事件所在的线程就不再是UI线程了,我们的onMessageEvent的ThreadMode就不能为默认值,必须指定为MAIN,确保其在主线程进行对UI的操作。
- **ThreadMode: BACKGROUND **
这种模式下,我们的订阅者将会在后台线程中执行。如果发布者是在主线程中进行的事件发布,那么订阅者将会重新开启一个子线程运行,若是发布者在不是在主线程中进行的事件发布,那么这时候订阅者就在发布者所在的线程中执行任务。
这种情况,一般是我们需要在订阅者方法中进行耗时的操作。
- ThreadMode: ASYNC
在这种模式下,订阅者将会独立运行在一个线程中。不管发布者是在主线程还是在子线程中进行事件的发布,订阅者都是在重新开启一个线程来执行任务。
这里我们可以简单的测试一下,加深理解:
下面代码用4种不同的Thread模式订阅同一事件MessageEvent
@Subscribe(threadMode = ThreadMode.PostThread)
public void onMessageEventPostThread(MessageEvent messageEvent) {
Log.e("PostThread", Thread.currentThread().getName());
}
@Subscribe(threadMode = ThreadMode.MainThread)
public void onMessageEventMainThread(MessageEvent messageEvent) {
Log.e("MainThread", Thread.currentThread().getName());
}
@Subscribe(threadMode = ThreadMode.BackgroundThread)
public void onMessageEventBackgroundThread(MessageEvent messageEvent) {
Log.e("BackgroundThread", Thread.currentThread().getName());
}
@Subscribe(threadMode = ThreadMode.Async)
public void onMessageEventAsync(MessageEvent messageEvent) {
Log.e("Async", Thread.currentThread().getName());
}
- 在主线程发布事件:
Log.e("postEvent", Thread.currentThread().getName());
EventBus.getDefault().post(new MessageEvent());
看一下日志输出:
可以看出,在主线程发布事件,那么Post和Main 模式下,订阅者方法也是在主线程执行,其他两种模式下分别是独立的线程。
- 在子线程发布事件
new Thread(new Runnable() {
@Override
public void run() {
Log.e("postEvent", Thread.currentThread().getName());
EventBus.getDefault().post(new MessageEvent());
}
}).start();
看一下日志输出:
子线程发布事件时,只有ThreadMode 为MAIN时,订阅者方法才会在主线程执行。其余都是在独立的线程。
sticky的意义####
这个单词sticky的意思是粘贴性,这里的意思就是在发送事件之后再订阅该事件也能收到该事件
默认为false。
priority 的意义####
这个priority的意义是优先级,这里主要说一下结论和注意事项
- 优先级高(priority值大)的将会首先接收到发布者所发布的事件
- 优先级只是针对于相同的ThreadMode中
- 默认的优先级为0
平时使用时,可以根据实际需求做调整。
订阅者索引相关###
在性能方面,EventBus 3由于使用了注解,比起使用反射来遍历方法的2.4版本逊色不少。但开启索引后性能远远超出之前的版本。下面就来看一下对于EventBus的另一种使用方式。
在Project的build.gradle中添加如下代码:
buildscript {
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
然后在app的build.gradle中添加如下代码:
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
apt {
arguments {
eventBusIndex "com.example.myapp.MyEventBusIndex"
}
}
重新rebuild之后会在build目录下面生成MyEventBusIndex文件,文件名可以自定义。下面就来看一下如何使用这个MyEventBusIndex.我们可以自定义设置自己的EventBus来为其添加MyEventBusIndex对象。代码如下所示:
EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();
我们也能够将MyEventBusIndex对象安装在默认的EventBus对象当中。代码如下所示:
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的前提下,在Android内部各个组件的交互通信可以说是杂乱无章,各种接口定义,注册、回调。搞得整个代码有着很严重的耦合,接口中多一个参数,参数类型变化都会导致很多地方要改。
这种思路注定了随着项目越来越庞大,耦合会越发的严重,所以势必需要一种新的思路来解决这个问题。
而EventBus的出现,很好的解决了这个问题
从这两幅图,我们可以清晰的感受到,EventBus给我们代码整体的结构带来多么大的益处,单从图上就可以看到明显减少了各个组件之间的耦合;同时从代码角度出发,EventBus短短几行代码,就可以实现之前通过接口-注册接口-回调 等一系列的工作才能完成的工作量。
当然,凡事要好必然有坏,EventBus使用方式的简单,也会导致我们在出现问题时无从下手,这就需要长时间的积累经验了。但是总的来说,还是利大于弊吧,所以当我们项目有大量的事件交互时,EventBus不失为一种好的选择!