EventBus 是 Android 的一个基于发布/订阅模式的轻量级框架,用于 Android 解耦的事件传递,开源地址。
这是官方地址的一个说明图:
可以看到,它是通过发布者的 post() 来发送时间,订阅者的 onEvent() 来响应事件,我们来看看怎么使用 EventBus:
第一步当然是导入它的依赖库:
compile 'org.greenrobot:eventbus:3.1.1'
首先我们需要注册 EventBus,注册有两种方式,一般使用第一种默认方式。
// 第一种,在 onCreate() 方法中注册
EventBus.getDefault().register(this);
// 第二种
EventBusBuilder busBuilder = EventBus.builder()
// 设置线程池,默认为动态多线程的线程池
.executorService(Executors.newSingleThreadExecutor())
// 事件是否继承,默认 true
.eventInheritance(true);
EventBus eventBus = busBuilder.build();
eventBus.register(this);
这里的事件是否继承是什么意思呢?如果事件 A 继承自事件 B,当发布者发布事件 A 时,如果可继承为 true,那么订阅者 B 也是可以收到事件 A 的,反之则接收不到。
我们需要注意的是,我们一定要记得在 onDestroy() 中去取消注册 EventBus:
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
首先我们先来定义一个事件:
public class Event {
private String data;
public Event(String data){
this.data = data;
}
public void setData(String data) {
this.data = data;
}
public String getData() {
return data;
}
}
然后我们需要发送事件:
// 发送事件
EventBus.getDefault().post(new Event("响应事件"));
响应事件:
// 通过注解方式响应事件
@Subscribe(threadMode = ThreadMode.POSTING,priority = 1,sticky = false)
public void onEvent(Event event ){
// 响应事件
}
这里的三个参数是什么意思呢?ThreadMode 是线程模式,我们看它的源码是一个枚举,有五种类型:
类型 | 说明 |
POSTING | 发布者在哪个线程发布事件,订阅者就在哪个线程响应事件 |
MAIN | 不管发布者在哪个线程发布事件,订阅者都在主线程响应事件。不同的是:当发布者在主线程时,订阅者会直接阻塞线程来响应事件,而当发布者在其它线程时,订阅者就会非阻塞的排队等待响应事件 |
MAIN-ORDERED |
不管发布者在哪个线程,订阅者都在主线程响应事件,与 MAIN 不同的是,MAIN-ORDERED 一直都是非阻塞的形式等待响应事件 |
BACKGROUND | 如果发布者不在主线程,那么订阅者直接在发布者的线程响应事件,如果发布者在主线程,那么订阅者就会使用一个单独的后台线程按序响应事件 |
ASYNC | 不管发布者在哪个线程,订阅者都是在一个独立的线程里面响应事件,发布者不会等待订阅者的响应,所以这种模式一般用于响应的事件比较耗时的情况 |
priority 是优先级,默认为 0,越大优先级越高;sticky 代表是不是粘性事件,默认为 false。
然后我们在另一个没有与发布者有任何代码关联的 Activity 来发送一个事件:
postEvent=findViewById(R.id.btn_post_event);
postEvent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().post(new Event("响应事件"));
}
});
效果图:
可以看到我们发送了事件过后,马上就响应了该事件。
上面我们说了一个黏性事件,这个是什么意思呢?我们来举一个栗子,大家想一下:如果我们发布了一个事件,但是订阅了这个事件的 Activity 还没有启动,那它肯定是不能响应该事件的,我们可以看看,我们在第一个页面发送事件 B,在第二个页面响应事件 B:
// 第一个 Activity 页面发送事件 B
postEvent=findViewById(R.id.btn_post_event_b);
postEvent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().post(new EventB("响应事件"));
}
});
// 第二个 Activity 页面响应事件 B
@Subscribe(threadMode = ThreadMode.POSTING)
public void onEvent(EventB eventB){
Toast.makeText(SubscriberEventActivity.this,eventB.getData(),Toast.LENGTH_SHORT).show();
}
效果图:
我们可以看到,第二个页面是没有响应事件的,这个时候黏性事件的作用就出来了,黏性事件的作用就是只要是发布者发送了事件,不管当时订阅者有没有接收到这个事件,只要订阅者来订阅这个事件就能接收到这个事件,怎么实现呢?
// 第一个 Activity 页面发送事件 B
postEvent=findViewById(R.id.btn_post_event_b);
postEvent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 通过 postSticky() 来发送粘性事件
EventBus.getDefault().postSticky(new EventB("响应事件B"));
}
});
// 第二个 Activity 页面响应事件 B,设置 sticky 为 true
@Subscribe(threadMode = ThreadMode.POSTING,sticky = true)
public void onEvent(EventB eventB){
Toast.makeText(SubscriberEventActivity.this,eventBus.getData(),Toast.LENGTH_SHORT).show();
}
效果图:
可以看到,就算我们当时不能订阅该事件,只要我们后面订阅该事件就能就收到该事件,但是就又出现一个问题了,它会只要重新进一次页面,重新订阅一次就会响应一次事件,那怎么办呢?我们就可以移除黏性事件:
// 第二个 Activity 页面响应事件 B,设置 sticky 为 true
@Subscribe(threadMode = ThreadMode.POSTING,sticky = true)
public void onEvent(EventB eventB){
Toast.makeText(SubscriberEventActivity.this,eventB.getData(),Toast.LENGTH_SHORT).show();
// 我们在响应该事件后就移除该黏性事件
EventBus.getDefault().removeStickyEvent(EventB.class);
}
效果图:
可以看到,我们移除黏性事件过后,就不会再响应该事件了。
EventBus 的简单使用就是这么多,那么我们使用 EventBus 需要注意什么呢?
1. EventBus 一般用于模块间通信,如果模块内能够适应 Intent、接口等通信时优先使用 Intent 通信
2. 因为模块开发一般是协同开发,所以对于事件的定义和响应要避免重复和包含
3. 因为 EventBus 是解耦的,所以就不要在响应事件里面发布事件来增加耦合性
4. 因为注册 EventBus 是默认动态多线程的线程池,所以要避免进行一些大量耗时操作的发布
5. 如果按照顺序处理事件,尽量保证发布者和订阅者在同一种线程,即使用 ThreadMode.POSTING
6. 要记得取消注册 EventBus,要不然它对 Activity 的持有会造成内存泄露