学完GreenDao之后再来学习下EventBus的使用,EventBus 是一个Android事件发布/订阅框架,它利用发布/订阅者者模式来对项目进行解耦,可以利用很少的代码,来实现多组件间通信/Android的组件间通信。
之前我们学习过一些传统的事件传递方式例如Handler、BroadcastReceiver等,相比之下EventBus代码简洁,使用简单,并将事件发布和订阅充分解耦。BroadcastReceiver常用于那些要与Android系统打交道的事件例如网络、电量的变化等;在BroadcastReceiver的 onReceive方法中可以获得Context 、intent参数,可以调用许多的sdk中的方法,EventBus就比较困难了。Handler一般用于线程间通信,它在发生问题时可以非常明确、快速的进行定位,通过msg.what就可以理清每一条消息流的逻辑,但是其内部类和其定义类是绑定的,造成了事件发布者和接受者之间的高耦合。EventBus的优势在于不像BroadcastReceiver那样依赖于 Context,也解除了Handler所带来的耦合,当然也同样有缺陷,当事件发布遭到大量滥用时,很难从事件发布者开始理清消息流,无法快速的找出是哪个订阅者接受并处理了消息导致的问题。不过凡事有利就有弊,这次就来学习一下EventBus的相关使用。
Event工作模式图如下图所示:
图中涉及到了三个角色:
- Event:事件,它可以是任意类型,EventBus会根据事件类型进行全局的通知。事件分为一般事件和Sticky事件,相对于一般事件,Sticky事件不同之处在于,当事件发布后,再有订阅者开始订阅该类型事件,依然能收到该类型事件的最近一个Sticky事件。
- Subscriber:事件订阅者,当有发布者发布这类事件后,EventBus会执行订阅者的onEven响应函数。订阅者通过register接口订阅某个事件类型,unregister接口退订。
- Publisher:事件发布者,可以在任意线程里发布事件。一般情况下,使用EventBus.getDefault()就可以得到一个EventBus单例对象,然后再调用post(Object)方法即可。
简单来说,Publisher(事件发布者)通过post()方法,把Event事件发布出去,Subscriber(事件订阅者)在onEvent()方法中接收事件。
那么我们就按照上述的说法来写个小Demo,这里为了简便,Subscriber与Publisher在同一Activity内:
定义事件1:EventSuccess,这里就自定义事件类
public class EventSuccess {
private int type;
private String message;
public EventSuccess(int type, String message) {
this.type = type;
this.message = message;
}
@Override
public String toString() {
return "type= "+type+" message= "+message;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
定义事件2:EventFailed
public class EventFailed {
}
MainActivity:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button success,fail;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);//在需要订阅事件的模块中,注册eventbus,不能重复注册;EventBus.getDefault得到单例
setContentView(R.layout.activity_main);
success = findViewById(R.id.success);
fail = findViewById(R.id.fail);
success.setOnClickListener(this);
fail.setOnClickListener(this);
textView = findViewById(R.id.textview);
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);//不用的时候一定要unregister
}
@Override
public void onClick(View v) {
//发送事件
switch (v.getId()){
case R.id.success:
EventSuccess event = new EventSuccess(1,"success");
EventBus.getDefault().post(event);
break;
case R.id.fail:
EventBus.getDefault().post(new EventFailed());
break;
}
}
//事件订阅者 处理事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void onSuccessEvent(EventSuccess event) {
textView.setText(event.toString());
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onFailEvent(EventFailed event) {
textView.setText("Fail!");
}
}
效果如下:
点击success:
点击fail:
这里说下@Subscribe参数,它其实可接收三个参数,ThreadMode、boolean sticky(也就是是不是sticky事件)、int priority(优先级)。参考EventBus 使用(全面分析,细节提醒)
-
ThreadMode 是用来决定onReceiveMsg将在哪种线程环境下被调用,共有5种Thread mode:
- POSTING :EventBus的默认模式,表示post事件是什么线程,onReceiveMsg接收事件方法就在同样的线程环境中执行代码
- MAIN:无论事件发布在什么线程,事件接收都在主线程中执行,这里分为两种情况:
(1) 事件发布者post事件在主线程,会阻塞post事件所在的线程。也就是如果连续post多个事件,当接收事件方法执行完才能post下
一个事件。
(2)事件发布者post事件不在主线程,那么这种情况下是非阻塞的。连续post多个事件,在主线程接收事件是耗时操作的话,执行的
流程会是非阻塞的。 - MAIN_ORDERED:无论事件发布者post在什么线程环境,它的执行流程是都非阻塞的,和MAIN模式下,post环境不是主线程的执行流程一样
- BACKGROUND:该模式下的时间发布者post线程环境与事件接收onReceiveMsg方法的线程环境关系如下:
(1)post发布环境是主线程的话,事件接收处理的环境是一个子线程。
(2)post发布环境是子线程的话,事件接收处理环境和post发布环境一样。 - ASYNC:该模式表示,无论post环境是什么线程,事件接收处理环境都是子线程。
-
sticky:一个boolean型的参数,默认值是false,表示不启用sticky特性。不同于之前的EventBus普通事件传递,sticky可以先post事件,后对订阅者注册。
接着上面的那个Demo:我们新增发布、注册按钮两个按钮,新增一个空的EventTest自定义事件类,并先将注册函数注释掉:
然后编写按钮点击逻辑:
可以看到这里就改成了postSticky,可以先发布事件,再注册:
效果如下:先点击发布按钮,再点击注册按钮
- priority:int型,默认值是0,代表优先级。当post事件发布,onEvent事件接收处理这两者的线程环境相同时,priority值越高,越先接收到事件post事件发布。