参考网站
环境配置
Gradle:
compile 'org.greenrobot:eventbus:3.0.0'
Maven:
<dependency>
<groupId>org.greenrobot</groupId>
<artifactId>eventbus</artifactId>
<version>3.0.0</version>
</dependency>
原理介绍
这张图是比较形象的EventBus的工作原理图,来自EventBus官网。我们可以形象的理解 EventBus 就是一个老司机,他有一个手册,手册上写了路线有很多个餐馆。这个时候有一个乘客上车,掏出手机给老师机看高德地图上的一条路线说我要去这儿,老司机和餐馆老板有私下交易,所以他会拉着你挨个经过这些餐馆。
上面有一些字是加粗的,老司机代表 EventBus,他在系统中是一个单例模式。所有的事件都要经过老司机,EventBus维护这一个HashMap,及一个手册,手册中记录了很多的路线,每条路线上有很多餐馆,路线就是我们的 事件(Message,这里不够形象,但是大概理解吧),好,路线上有很多个餐馆,餐馆就是 订阅者,源码中直接用这样的结构存储 Map<Object, List<Class<?>>>
,Object就是事件, List 里面存的订阅者,当某种事件发生时,这条线上的所有订阅者都会收到消息。至于还有一个乘客,发布事件的人,其实就是 EventBus 本身,通过 EventBus.getDefault.post( Message ) 发送事件。
不知你理解了没有,反正我理解了。如果不理解,那就看代码吧!
入门例子
1. 定义 MessageEvent 对象
MessageEvent.java
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
2. 定义 处理 MessageEvent对象方法
MainActivity.java
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
3. 注册和取消订阅
MainActivity.java
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
4. 发布MessageEvent
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
理清思路
根据代码,我们再来理解刚开始给的那张图。如果使用EventBus这个老司机,那么系统中就只会出现这么一个老司机,它是单例模式,猜都能猜到,如果不信,你可以跟到源代码EventBus.getDefault()
中看,很基本的双重锁写法:
static volatile EventBus defaultInstance;
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
然后通过 老司机.post(MessageEvent event) 出来的事件首先会传给老司机,因为系统中只有老司机知道有哪些订阅者,并且他们订阅的是什么事件,他是通过注解的形式知道,@Subscribe
表示该方法是要处理事件的,但是处理哪种事件呢?后面方法的参数决定 MessageEvent event
。当然还不要忘了一定要给老司机 注册他才认识你 EventBus.getDefault().register(this);
,看源码发现在在调用register的时候,EventBus才会去找这个类中的 @Subscribe 。
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
然后老司机拿到了 事件,并且看了看他的注册表中有哪些家伙订阅了这个事件,当事件发生时,就把事件传给他们,让他们去处理。我觉得这样写应该所有人都能明白吧,至于底层的原理,可以看看EventBus的源码。
ThreadMode 介绍
还记得上面的HelloWorld例子中我们用到了 @Subscribe(threadMode = ThreadMode.MAIN)
, MAIN 表示UI主线程,EventBus包括以下4种:
ThreadMode.POSTING:默认模式,效率高,收到消息就执行。让订阅方法工作在与发布消息同一个线程中。
ThreadMode.MAIN:让订阅方法始终切换到 UI主线程中执行
ThreadMode.BACKGROUND:启动新线程,让订阅方法在新线程中执行
ThreadMode.ASYNC:异步模式,也是工作在新线程中,但是EventBus提供了线程池的管理,避免线程不停创建。
其实ThreadMode就是为了让我们更好的在线程中切换,大家都知道Android中UI主线程后后台线程的区别,以前我们都要使用 Handler,AsyncTask 等来处理,代码繁重,现在只需要一个注解就搞定了,是不是很爽。
这也让我想到了 RxJava 中也提供了线程切换这样的功能,流式编程就显得更加的简洁明了,并且还不需要这样那样的注册,大总管之类的东西,所以现在大家都喜欢用RxJava。
EventBus其他功能介绍
StickyEvent
第一个是粘性事件,普通的事件我们通过post发送给EventBus,发送过后之后当前已经订阅过的方法可以收到。但是如果有些事件需要所有订阅了该事件的方法都能执行呢?例如一个Activity,要求它管理的所有Fragment都能执行某一个事件,但是当前我只初始化了3个Fragment,如果这时候通过post发送了事件,那么当前的3个Fragment当然能收到。但是这个时候又初始化了2个Fragment,那么我必须重新发送事件,这两个Fragment才能执行到订阅方法。
粘性事件就是为了解决这个问题,通过 postSticky 发送粘性事件,这个事件不会只被消费一次就消失,而是一直存在系统中,知道被 removeStickyEvent 删除掉。那么只要订阅了该粘性事件的所有方法,只要被register 的时候,就会被检测到,并且执行。订阅的方法需要添加 sticky = true 属性。
EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {
textField.setText(event.message);
}
订阅权限 Subscriber Priorities
你可以通过 在@Subscribe(priority = 1)
中添加 priority 来定义订阅的权限。权限越高的方法会在收到后越先执行。默认的 priority = 0
@Subscribe(priority = 1)
public void onEvent(MessageEvent event) {
...
}
取消事件分发
当你收到一个事件,并且调用 cancelEventDelivery
后,那么这个事件不会再往下分发,要知道,有可能不止一个方法订阅了该消息哦,取消后后面的方法就都收不到消息了。
// Called in the same thread (default)
@Subscribe
public void onEvent(MessageEvent event){
// Process the event
...
// Prevent delivery to other subscribers
EventBus.getDefault().cancelEventDelivery(event) ;
}
还有其他的就自己看官网吧
写在最后的话
我也是才看EventBus,其实用起来的话感觉确实比以前的Handler那些机制要好用。但是EventBus这个大总管的方式其实我不太喜欢,如果项目不停迭代,代码越来越多,管理Message就变得很繁琐,出错也不好排查。而且这个大总管是单例哦,如果App一直运行,或并发数目比较多时,可能性能上会有一些问题,当然这只是自己的猜想而已,我还没有实践过。
老司机开开车,其实我不是老司机,学习一点内容,分享分享,希望能抛砖引玉。自己也写过很多的内容,总结下来也有一些经验,对于我们这些初学者来说,请记住一定学习一个新技术,新框架的时候,从官网入手,英文就英文,多看两遍就回了。所以我写过的每一篇笔记都是附上官网地址的,所以你还在等什么,看官网去吧,光看这点是不够的。