EventBus除了可以发送普通事件,还可以支持发送粘性事件,就是先发送事件之后再订阅也能收到该事件,类似于粘性广播,第三篇我们就来解析一下黏性事件的原理。

Android 轻量级线程间通信EventBusAndroid EventBus保姆级源码解析(一)注册方法registerAndroid EventBus保姆级源码解析(二)发送事件postAndroid EventBus保姆级源码解析(三)黏性事件原理

总结

惯例总结(太长不看版):

  • 黏性事件是通过stickyEvents这个Map去实现的
  • 所有post/remove的黏性事件会被保存/删除到stickyEvents
  • 当订阅了黏性事件(Event)时,发现stickyEvents中存在这个Event,则直接调用订阅方法

使用黏性事件

首先我们回顾一下黏性事件的使用:

  1. 订阅者订阅粘性事件
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
    fun XXX(MessageEvent messageEvent){
        ···
    }
  1. 发送粘性事件
EventBus.getDefault().postSticky(new MessageEvent())
  1. 移除黏性事件
EventBus.getDefault().removeStickyEvent(new MessageEvent())

可以很明显看到黏性事件和普通事件的区别在于订阅时在注解中使用sticky = true、以及使用EventBus.postSticky方法,我们先看EventBus.postSticky有什么不同

EventBus.postSticky
/**
     * Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
     * event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
     */
    public void postSticky(Object event) {
        // 获取到锁之后把事件put进黏性事件数组stickyEvents
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        // 直接调用post方法
        post(event);
    }

哦,原来EventBus.postSticky方法仅仅是在EventBus.post之前把黏性事件添加到stickyEvents队列了而已,EventBus.post方法我们在上一篇中有详细的讲解

Android EventBus保姆级源码解析(二)发送事件post

第二篇post的解析中,并没有发现对黏性事件的任何处理,那黏性事件是怎么实现的呢,仔细想想黏性事件的需求,先发送事件、再订阅也能收到该事件,所以是不是订阅的时候也有额外处理呢,没错第一篇register方法中我们挖了一个坑:

在订阅时,Event.register会调用

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        ...
        Object stickyEvent = stickyEvents.get(eventType);
        // post黏性事件
        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        ...
    }

其中newSubscription就是当前订阅类、订阅方法的包装类,checkPostStickyEventToSubscription来处理这个包装类

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
            //如果黏性事件列表不为空直接调用postToSubscription
            postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
        }
    }

如果黏性事件列表不为空直接调用postToSubscription去在对应的线程调用订阅方法,这个方法详细解析也可以看第二篇,到此EventBus中所有调用黏性事件的方法就讲完了

没错,就是这么简单, Event通过维护几个数组

  • 黏性数组(stickyEvent)
  • <订阅class,<订阅类,订阅方法>list>的Map(subscriptionsByEventType)
  • <订阅类,订阅的类列表>Map(typesBySubscriber
  • Local包装的当前线程事件列表currentPostingThreadState

这么简单的完成了线程间的通信,使用起来很也无感、轻量,不得不让人佩服作者的水平

那,这篇就到这了,其实EventBus中还有怎么通过反射获取@Subscribe注解的方法、建筑者模式值得一讲,那就有空再聊了(他挖坑一直可以的)