首页 > 代码库 > EventBus 这一篇还不够

EventBus 这一篇还不够

  • 参考网站
  • 环境配置
  • 原理介绍
  • 入门例子
    • 定义 MessageEvent 对象
    • 定义 处理 MessageEvent对象方法
    • 注册和取消订阅
    • 发布MessageEvent
    • 理清思路
  • ThreadMode 介绍
  • EventBus其他功能介绍
    • StickyEvent
    • 订阅权限 Subscriber Priorities
    • 取消事件分发
    • 还有其他的就自己看官网吧
  • 写在最后的话

参考网站

EventBus 官网

环境配置

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一直运行,或并发数目比较多时,可能性能上会有一些问题,当然这只是自己的猜想而已,我还没有实践过。

老司机开开车,其实我不是老司机,学习一点内容,分享分享,希望能抛砖引玉。自己也写过很多的内容,总结下来也有一些经验,对于我们这些初学者来说,请记住一定学习一个新技术,新框架的时候,从官网入手,英文就英文,多看两遍就回了。所以我写过的每一篇笔记都是附上官网地址的,所以你还在等什么,看官网去吧,光看这点是不够的。

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    EventBus 这一篇还不够