EventBus
1、什么是EventBus?
EventBus,中文翻译事件总线,是greenrobot开发的基于发布-订阅模型的开源框架。可以理解为,将一个个的event事件发送到bus总线上,然后eventbus会根据订阅者已匹配的事件,然后把事件发送给对应的订阅者。我们在activity之间通信的时候,可以使用intent或者handler的方式进行通信。但是代码复杂,且效率较低,因此出现了EventBus。
2、什么是发布-订阅模型?
其实最初的24种设计模式中,并没有发布-订阅模式,而是观察者模式的一种变种,但随着时间的推移,现已经独立与观者者模式。如果说观察者模式可以形象地类比为微信公众号发布新文章时,会直接通知所有关注它的用户;而发布-订阅模型可以当作一个作家发表文章的时候,不会直接将文章寄给读者,而是会交给报社,由报社定期发布报纸给所有读者。
我们可以发现其中的区别在于,观察者模式中,observer直接和subject接触,而在发布-订阅模式则存在一个中间的发布通道,防止订阅者和发布者产生依赖关系,如下图所示。
观察者模式,实际上是为了实现松耦合。而发布订阅模式。,由于存在中间者,则不存在耦合关系。
3、EventBus的使用
目前我们使用的场景是在两个activtiy中传递数据,由Activity A打开Activity B,并在Activity B更改Activity中的ui,内容由Activity B决定。
(1)导入依赖
api 'org.greenrobot:eventbus:3.0.0'
(2)然后创建Event类,用于传递数据。
public class MessageEvent {
private String message;
public MessageEvent(String message){
this.message = message;
}
public String getMessage(){
return message;
}
}
(3)在Activity A中注册EventBus,目的是将A变成subscriber,从而能够订阅发来的事件消息。
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);
}
此外,当不需要的时候,需要进行注销。
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
(4)注册完成后,还需要增加接受事件,以及对应处理逻辑的方法。这里表示当收到MessageEvent,会由这个方法接受事件,并做对应的处理。
//订阅方法,当接收到事件的时候,会调用该方法
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent messageEvent){
Log.d("cylog","receive it");
textView.setText(messageEvent.getMessage());
Toast.makeText(MainActivity.this, messageEvent.getMessage(), Toast.LENGTH_SHORT).show();
}
这里的@Subscribe注解,用于表示该方法为订阅方法。至于后面threadMode = ThreadMode.MAIN,放在后文进行更加详细的讲述。此外EventBus3.0能支持自定义的方法名,而不一定需要以onEvent进行命名。
4、发布事件
在Activity B需要的地方调用 EventBus.getDefault().post(EventType eventType);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_main);
Button button = (Button) findViewById(R.id.sendMessageBtn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().post(new MessageEvent("Hello !....."));
}
});
}
这样就可以将包含"Hello !.."的事件传递给Activity A的onEvent中,从而改变Activity中textview的文本。
注:当发出事件时,所有接受参数类型MessageEvent的
5、@Subscribe
打开注解的源代码,可以看到有三个成员变量,分别是threadMode、sticky、priority。
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
/**
* If true, delivers the most recent sticky event (posted with
* {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
*/
boolean sticky() default false;
/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */
int priority() default 0;
}
(1)threadMode代表订阅方法所在的线程,它所属的ThreadMode枚举类有四个类型,分别是
POSTING 代表订阅方法和发布事件在同一进程内,这也是默认的类型。适用于执行事件短且不需要主线程的任务。
MAIN 代表订阅方法在主线程中执行,如果发布事件的线程在主线程,则将直接执行订阅方法。
以上两种都必须是短时间的,否则将会阻塞发布线程(POSTING如果发布线程是主线程,那么POSTING订阅方法执行时间过长,则可能导致主线程阻塞;而MAIN直接运行在主线程中)
BACKGROUND 订阅方法将在后台线程执行,如果发布线程也在后台则直接执行。如果发布线程在主线程,则EventBus将会创建一个后台线程,按顺序交付所有的事件。
ASYNC 订阅方法将执行在独立的线程中,区别于发布线程和主线程,本模式适合需要执行长时间的任务,同时应该避免同时开启大量的发布,因为将会同时并发存在很多线程。这个模式使用的线程池对线程进行复用。
(2)priority 优先级
可以通过设置优先级,来使得优先级高的会优先接收到事件。
(3)sticky 粘性事件
此概念类似于广播中的粘性广播,可以先将事件发出,留存在内存中,后面等到有订阅方法时,再接受订阅。类似如果读者还没出现,报社(发布者)会将报纸放在书报亭(内存),等到有读者出现的时候自己去买报纸。
注:只能接受到最后发出的一条粘性事件。
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.sendStickyMessageBtn1:
EventBus.getDefault().postSticky(new MessageEvent("粘性事件1"));
Log.d("cylog","发送粘性事件1...");
break;
case R.id.sendStickyMessageBtn2:
EventBus.getDefault().postSticky(new MessageEvent("粘性事件2"));
Log.d("cylog", "发送粘性事件2...");
break;
case R.id.sendStickyMessageBtn3:
EventBus.getDefault().postSticky(new MessageEvent("粘性事件3"));
Log.d("cylog", "发送粘性事件3...");
break;
case R.id.sendRegisterBtn:
Log.d("cylog", "注册成为订阅者...");
EventBus.getDefault().register(this);
break;
}
}
@Subscribe(sticky = true)
public void onEvent(MessageEvent messageEvent){
Log.d("cylog","接受到了来自EventBus的事件:"+messageEvent.getMessage());
}
按顺序点击 1、2、3、4个按钮,如果不设置粘性,发布事件先于注册订阅方法,因此订阅方法无法接受到,自然不会答应对应的事件。如果设置了粘性后,仍然以1、2、3、4的顺序点击,最后log打印时,只会答应发出的最后一条粘性广告,即打印事件3。