EventBus 是一款针对 Android 端优化的 publish/subscribe 消息总线。
主要目的是替代Intent, Handler,BroadCast 在 Fragment,Activity,Service,线程之间传递消息,优点是开销小,代码优雅。将发送者和接收者解耦。
基本用法
引入 eventbus:
compile 'de.greenrobot:eventbus:2.4.0'
定义事件:
public class MessageEvent { /* Additional fields if needed */ }
注册事件接收者:
eventBus.register(this);
发送事件:
eventBus.post(event)
接收消息并处理:
public void onEvent(MessageEvent event) {}
注销事件接收:
eventBus.unregister(this);
最后,proguard 需要做一些额外处理:
# EventBus
-keepclassmembers class ** {
public void onEvent*(**);
void onEvent*(**);
}
基本概念
作为一个消息总线,有三个主要的元素:
- Event:事件。可以是任意类型的对象
- Subscriber:事件订阅者,接收特定的事件。在EventBus中,使用约定来指定事件订阅者以简化使用。即所有事件订阅都都是以onEvent开头的函数,具体来说,函数的名字是onEvent,onEventMainThread,onEventBackgroundThread,onEventAsync这四个,这个和ThreadMode有关
- Publisher:事件发布者,用于通知 Subscriber 有事件发生。可以在任意线程任意位置发送事件,直接调用 eventBus.post(Object) 方法,可以自己实例化 EventBus 对象,但一般使用默认的单例就好了:EventBus.getDefault(), 根据 post 函数参数的类型,会自动调用订阅相应类型事件的函数。
指定处理线程
默认情况下,onEvent 方法会在 post(Event) 的方法的线程中执行,但是可能会 post(Event) 是一个非UI线程,而 onEvent 方法需要更新UI,需要在主线程运行。
public void onEventMainThread(MessageEvent event) {
textField.setText(event.message);
}
因为每个事件订阅函数都是和一个 ThreadMode 相关联的,ThreadMode 指定了会调用的函数。有以下四个 ThreadMode:
- PostThread:事件的处理在和事件的发送在相同的进程,所以事件处理时间不应太长,不然影响事件的发送线程,而这个线程可能是UI线程。对应的函数名是 onEvent。
- MainThread: 事件的处理会在UI线程中执行。事件处理时间不能太长,这个不用说的,长了会ANR的,对应的函数名是onEventMainThread。
- BackgroundThread:事件的处理会在一个后台线程中执行,对应的函数名是 onEventBackgroundThread,虽然名字是 BackgroundThread,事件处理是在后台线程,但事件处理时间还是不应该太长,因为如果发送事件的线程是后台线程,会直接执行事件,如果当前线程是 UI 线程,事件会被加到一个队列中,由一个线程依次处理这些事件,如果某个事件处理时间太长,会阻塞后面的事件的派发或处理。
- Async:事件处理会在单独的线程中执行,主要用于在后台线程中执行耗时操作,每个事件会开启一个线程(有线程池),但最好限制线程的数目。
使用 Sticky Events
某些时候,我们并不希望发布出来的Event立即被消费掉,而是等到时机成熟。比如说,在一个详情页点赞之后,产生一个VoteEvent,VoteEvent并不立即被消费,而是等用户退出详情页回到商品列表之后,接收到该事件,然后刷新Adapter等。其实这就是之前我们用startActivityForResult和onActivityResult做的事情。
那么怎么用EventBus来模拟这样的情况呢?这就需要另外一个强大的机制,Sticky Event.
和普通的Event不同,当发布一个Sticky Event,需要使用postSticky方法:
EventBus.getDefault().postSticky(new VoteEvent(obj));
上面的代码发布了一个点赞的Sticky Event.
...
EventBus.getDefault().registerSticky(this);
...
public void onEventMainThread(VoteEvent event) {
// TODO update adapter
}
EventBus 的 registerSticky 方法在每次执行的时候,都会去查询一次 Sticky Event,并调用响应的方法处理。
除了 registerSticky 方法来触发Sticky Event 之外,我们还可以通过 getStickyEvent 方法来获取响应的 Sticky Event 来进行处理。比如,我们可以在 onResume 方法里面检查 EventBus 里面是否有 VoteEvent 的Sticky Event,从而进行处理:
@Override
public void onResume() {
super.onResume();
VoteEvent voteEvent = EventBus.getDefault().getStickyEvent(VoteEvent.class);
}
3.0 beta 版本的变化
3.0 版本目前还在 beta 阶段,主要变化时使用注解代替基于惯例的 onEvent 方法,用法如下:
@Subscribe(threadMode = ThreadMode.MainThread)
public void onVoteEvent(VoteEvent event) {
}
进阶
想要探索更幽深的世界,就去看源码吧: