EventBus 3.2.0 源码阅读

版本

v3.2.0

源码+注释存放在

参考:

  • 官网
  • 这是一份详细的 EventBus 使用教程
  • EventBus版本变更图

读前简介

1. EventBus中的各个角色

  • 发布者
  • 订阅者
  • 订阅方法
  • 事件
  • 发布线程
  • 订阅线程
  • 事件Hash表

2.主要类功能及名词介绍

  • SubscriberMethod:订阅方法的封装
  • EventBus : EventBus框架的入口,提供 注册,反注册 ,发送消息,配置能力
  • EventBusBuilder:EventBus的构建器
  • SubscriberMethodFinder:订阅方法查找器 ,用于获取
  • Subscription:作为一个订阅的封装 ,拥有订阅者 和 订阅方法
  • HandlerPoster:一个Handler 如果 传入looper 是主线程 那就是主线程 handler
  • 黏性事件:就是在发送事件之后再订阅该事件也能收到该事件,用于解决异步所带来的一些问题

源码阅读顺序

  1. 获取EventBus实例
  2. 注册 及 注册订阅方法
  3. 反注册
  4. 发送消息 及 接受消息
  5. 取消事件
  6. 黏性事件
  7. 线程切换

下面我们就按照这个顺序,依次看一下EventBus 都做了哪些事情

开始阅读

一. 获取EventBus实例

1. EventBus.getDefault()

EventBus.getDefault()就是通过 DCL 方式 创建单例。通过这种方式获取的消息线路,我们称之为 ==消息总线==

    static volatile EventBus defaultInstance;

    //使用DCL 方式创建单例 
    public static EventBus getDefault() {
        EventBus instance = defaultInstance;
        if (instance == null) {
            synchronized (EventBus.class) {
                instance = EventBus.defaultInstance;
                if (instance == null) {
                    instance = EventBus.defaultInstance = new EventBus();
                }
            }
        }
        return instance;
    }
2. new EventBus()

除了使用 EventBus.getDefault() 获取单例 以外,EventBus 还支持通过 new 的方式创建新的EventBus 对象,不过不同的EventBus 对象对应着不同的 消息线路,也就是说 ==A== EventBus 不可能 接收到 ==B== EventBus 的 消息。所以如果总线压力过大,我们可以 通过多个EventBus 来分担 总线的压力.

3. new EventBus(EventBusBuilder builder)

除了使用默认构造这种简单的创建方式以外,EventBus 还支持通过 构建器来创建 实例。其实默认构造最终也是调用的构建器模式,只不过它的构建器也是默认的。如下代码所示

    private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
    
    public EventBus() {
        this(DEFAULT_BUILDER);
    }

    EventBus(EventBusBuilder builder) {
    .....
    }

下来我们看看怎么通过构建器创建EventBus 对象

  EventBusBuilder builder = EventBus.builder();
        builder.eventInheritance=false;
        builder. ..
        EventBus eventBus = builder.installDefaultEventBus();
        eventBus. ..

其实就是通过 构建器 设置一些默认的值 ,然后在构建出一个 EventBus 对象。非常正统的构建器模式。下来我们看一下 EventBus 都给我们预留了那些 接口。可以让我们自行配置。

4. EventBusBuilder 配置
属性---含义---默认值的对应关系(具体功能介绍请看下方)
属性 含义 默认值
logSubscriberExceptions 订阅函数执行有异常时,打印异常信息 true
logNoSubscriberMessages 是否打印没有 订阅函数 的log true
sendSubscriberExceptionEvent 订阅函数执行出错 是是否发送 一个异常类型为 SubscriberExceptionEvent 的消息 方便统一常 true
sendNoSubscriberEvent 没有对应订阅函数是发送一个类型为 NoSubscriberEvent 的消息 方便统一 true
throwSubscriberException 订阅函数执行出错时抛出 EventBusException 异常,当为true时 logSubscriberExceptions,sendSubscriberExceptionEvent 这俩就没用了 false
eventInheritance 是否触发 订阅函数形参 为消息类型的父类的订阅函数(官方描述 如果关闭会 提升20的速度,具体时间取决于 注册类的继承结构) true
ignoreGeneratedIndex 忽略订阅者索引(既是存在订阅者索引,也强制使用反射获取订阅方法) false
strictMethodVerification 是否严格验证订阅函数签名 false
executorService 自定义线程池 DEFAULT_EXECUTOR_SERVICE
skipMethodVerificationForClasses 跳过方法签名验证集合
subscriberInfoIndexes 添加订阅者索引(不指定的话EventBus就会通过反射的方式获取 注册方法 列表)
logger EventBus 内部使用的 Logger
具体功能解析
  • logSubscriberExceptions:订阅函数执行有异常时,打印异常信息
    当为true是如果订阅函数内部异常的话 会在控制台打印 error 类型的log,如下log,如果为false则不打印。
2020-06-15 11:58:11.380 26367-26367/org.greenrobot.eventbusperf E/EventBus: Could not dispatch event: class java.lang.String to subscribing class class org.greenrobot.debug.BadExceptionSubscriber
    java.lang.RuntimeException: Bad
        at org.greenrobot.debug.BadExceptionSubscriber.onEvent(BadExceptionSubscriber.java:8)
        at java.lang.reflect.Method.invoke(Native Method)
        at org.greenrobot.eventbus.EventBus.invokeSubscriber(EventBus.java:511)
        at org.greenrobot.eventbus.EventBus.postToSubscription(EventBus.java:434)
        .....
  • logNoSubscriberMessages : 是否打印没有监听者的log
    当为true时:如果没有对应的监听者会在 控制台打印 如下log。如果为false就没有
2020-06-15 11:02:43.656 14516-14516/org.greenrobot.eventbusperf D/EventBus: No subscribers registered for event class java.lang.String
2020-06-15 11:02:43.656 14516-14516/org.greenrobot.eventbusperf D/EventBus: No subscribers registered for event class org.greenrobot.eventbus.NoSubscriberEvent
  • sendSubscriberExceptionEvent : 订阅函数执行出错 是是否发送 一个异常类型为 SubscriberExceptionEvent 的消息 方便统一处理异常
    当为true时遇到 订阅函数执行有异常的时候会发送一个异常类型为 SubscriberExceptionEvent 的消息。这时候我们可以进行降级处理 或者 上报异常 操作如下
  @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(SubscriberExceptionEvent event) {
        Log.e(TAG,"订阅函数有异常 "+event.toString());

    }
  • sendNoSubscriberEvent: 没有对应订阅函数是发送一个类型为 NoSubscriberEvent 的消息 。这时候我们可以进行降级处理 或者 上报异常 操作如下
  @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(NoSubscriberEvent event) {
        Log.e(TAG,"没有订阅函数 "+event.toString());

    }
  • throwSubscriberException:订阅函数执行出错时抛出 EventBusException 异常,当为true时 logSubscriberExceptions,sendSubscriberExceptionEvent 这俩就没用了,因为在 EventBus 中如下代码中 throwSubscriberException 的优先级比 logSubscriberExceptions,sendSubscriberExceptionEvent 这俩高
 private void handleSubscriberException(Subscription subscription, Object event, Throwable cause) {
        if (event instanceof SubscriberExceptionEvent) {
            ....
        } else {
            if (throwSubscriberException) {
                throw new EventBusException("Invoking subscriber failed", cause);
            }
            if (logSubscriberExceptions) {
                logger.log(Level.SEVERE, "Could not dispatch event: " + event.getClass() + " to subscribing class "
                        + subscription.subscriber.getClass(), cause);
            }
            if (sendSubscriberExceptionEvent) {
                SubscriberExceptionEvent exEvent = new SubscriberExceptionEvent(this, cause, event,
                        subscription.subscriber);
                post(exEvent);
            }
        }
    }
  • eventInheritance :是否触发 订阅函数形参 为消息类型的父类的订阅函数(有点绕,看下面例子就很清晰)官方描述 如果关闭会 提升20的速度,具体时间取决于 注册类的继承结构。
    
        // BEventBean 继承了 AEventBean
        public class BEventBean extends AEventBean {
            public String bMsg;
        }
    
    
        @Subscribe(threadMode = ThreadMode.MAIN)
        public void onEventStr(AEventBean msg) {
            log(msg.toString());
        }
    
    
        @Subscribe(threadMode = ThreadMode.MAIN)
        public void onEventStr(BEventBean msg) {
            toast(msg.toString());
        }
    
    ==注: BEventBean 继承了 AEventBean==
    • 当 eventInheritance 为true的时候上面 两个订阅方法都会被执行。
    • 当 eventInheritance 为false的时候只有形参为 BEventBean 会被执行。
  • ignoreGeneratedIndex:即使存在订阅者索引,也强制使用反射获取订阅方法。
    这个标示位使用在SubscriberMethodFinder.findSubscriberMethods 方法中.
   List findSubscriberMethods(Class subscriberClass) {
        List subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }

        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }
  • strictMethodVerification: 是否严格验证订阅函数签名,默认为false,当为true时 EventBus 当检测到 注册方法的签名不合法时会抛出异常,代码位于 SubscriberMethodFinder.findUsingReflectionInSingleClass
  • executorService:可以设置项目自己的线程池 如果有的话 ,避免 项目中有多个箱尺寸在,浪费资源
  • skipMethodVerificationForClasses :跳过方法签名验证集合,通过 skipMethodVerificationFor(Class clazz) 可以添加
  • subscriberInfoIndexes:添加订阅者索引,方便我们自定义和扩展
  • logger:EventBus 内部使用的 Logger ,可以使用 logger()方法进行设置

二. 注册 及 注册订阅方法

主要作用

  1. 查找订阅者中的订阅方法并缓存到 EventBus.subscriptionsByEventType 这个map中
  2. 如果是黏性订阅方法 订阅以后直接 调用。以实现先 发消息后注册 也能接收到 事件的功能

源码梳理

1. EventBus.register
    //注册 订阅者
    public void register(Object subscriber) {
        //获取接受者的字节码对象
        Class subscriberClass = subscriber.getClass();
        //查找 subscriber 中的订阅方法 ,这里没有加锁 说明可以并发进行 【详见1.1】
        List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            //遍历所有订阅方法
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                //预处理每个 订阅方法 【详见1.2】
                subscribe(subscriber, subscriberMethod);
            }
        }
    }
1.1 SubscriberMethodFinder.findSubscriberMethods

主要作用是 在订阅者中查找订阅方法列表 并进行返回,查找方式分为两种

  1. 反射
  2. 订阅索引

使用订阅索引会提高EventBus的运行速度,具体原理和如何使用参考 ==知识点3,4==

 List findSubscriberMethods(Class subscriberClass) {
        //从缓存中获取
        List subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            //命中缓存直接返回
            return subscriberMethods;
        }

        //是否强制使用反射获取订阅方法(可在EventBusBuilder 中配置)
        if (ignoreGeneratedIndex) {//使用反射
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {//使用索引查找
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }
1.2 EventBus.subscribe

主要有两个

  1. 将订阅关系缓存到 subscriptionsByEventType 和 typesBySubscriber中
  2. 如果是黏性事件 直接调用 订阅方法
 private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
         ....
         //添加 订阅方法形参类型 和 一个订阅 的映射
         subscriptionsByEventType.put(eventType, subscriptions);

         ....
         //添加 订阅者 和 订阅方法形参类型 的映射
         typesBySubscriber.put(subscriber, subscribedEvents);
         
         ....

        //如果是黏性订阅方法 订阅以后直接 调用,
        if (subscriberMethod.sticky) {
            //是否触发 订阅函数形参 为消息类型的父类的订阅函数,可在 EventBusBuilder 中配置
            if (eventInheritance) {//是

                Set, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry, Object> entry : entries) {
                    Class candidateEventType = entry.getKey();
                    //candidateEventType 是否是 eventType 的子类
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {//否
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

三. 反注册

主要作用

主要作用是 将在订阅过程中形成的订阅关系同 缓存中清除 ,清除的位置有两个

  1. subscriptionsByEventType
  2. typesBySubscriber

源码梳理

1. EventBus.unregister
   public synchronized void unregister(Object subscriber) {
        List> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class eventType : subscribedTypes) {
                //移除各个订阅
                unsubscribeByEventType(subscriber, eventType);
            }
            // 移除 订阅者 和 订阅方法形参类型 的映射关系
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

三. 发送消息 及 接受消息

主要作用

发送事件并触发订阅方法

源码梳理

1. EventBus.post
 public void post(Object event) {
        //获取到当前线程的 PostingThreadState(用 ThreadLocal 保存 所以每个 线程只有一个)
        PostingThreadState postingState = currentPostingThreadState.get();
        //获取当前线程的 事件队列
        List eventQueue = postingState.eventQueue;
        //将事件添加到事件队列中
        eventQueue.add(event);

        if (!postingState.isPosting) {//没有正在发送事件
            ....
            try {
                while (!eventQueue.isEmpty()) {//队列不为空
                    //发送事件 【详见1.1】
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {//重置状态
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

 
 
1.1 EventBus.postSingleEvent
 private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        //获取事件类型
        Class eventClass = event.getClass();
        boolean subscriptionFound = false;
        //是否 触发 event 父类的 注册方法
        if (eventInheritance) {
            List> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) {
                Class clazz = eventTypes.get(h);
                //【详见1.2】
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            //【详见1.2】
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        if (!subscriptionFound) {//没找到订阅方法
            if (logNoSubscriberMessages) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                //发送一个 NoSubscriberEvent
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

1.2 EventBus.postSingleEventForEventType
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class eventClass) {
        CopyOnWriteArrayList subscriptions;
        synchronized (this) {
            //通过 订阅方法形参类型 获取 订阅列表
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {//查找到了
            for (Subscription subscription : subscriptions) {
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted;
                try {
                    //触发所有订阅方法 【详见1.3】
                    postToSubscription(subscription, event, postingState.isMainThread);
                     // 执行 cancelEventDelivery 后 postingState.canceled 会为true
                    aborted = postingState.canceled;
                } finally {
                    postingState.event = null;
                    postingState.subscription = null;
                    postingState.canceled = false;
                }
                //如果执行了 cancelEventDelivery 就会退出,不在 触发其他订阅方法
                if (aborted) {
                    break;
                }
            }
            return true;
        }
        return false;
    }

1.3 EventBus.postToSubscription
 private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING://直接触发
                invokeSubscriber(subscription, event);
                break;
            case MAIN://如果不是主线程 切换到主线程触发
                if (isMainThread) {
                    //详见【1.4】
                    invokeSubscriber(subscription, event);
                } else {//不是主线程的话 会 通过handle调度到主线程执行
                    //详见【1.5】
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND:
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }
1.4 EventBus.invokeSubscriber
    void invokeSubscriber(Subscription subscription, Object event) {
        try {
            //反射执行 订阅方法
            subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
        } catch (InvocationTargetException e) {
            handleSubscriberException(subscription, event, e.getCause());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Unexpected exception", e);
        }
    }
1.5 HandlerPoster.enqueue
    public void enqueue(Subscription subscription, Object event) {
        //将 subscription , event 封装为 PendingPost
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            //加入到队列中
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                //使用handler 发送一个消息 【详见1.6】
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }
1.6 HandlerPoster.handleMessage

 @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            while (true) {
                //取出一个 事件
                PendingPost pendingPost = queue.poll();
                ....
                //执行 订阅方法 【详见1.4】
                eventBus.invokeSubscriber(pendingPost);
                ....
            }
        } finally {
            handlerActive = rescheduled;
        }
    }

四. 取消事件

主要作用

取消以后就不会触发 后面的 订阅方法

源码梳理

1 HandlerPoster.cancelEventDelivery
 public void cancelEventDelivery(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();
        //只有  threadMode = POSTING 时有效
        if (!postingState.isPosting) {
            throw new EventBusException(
                    "This method may only be called from inside event handling methods on the posting thread");
        } else if (event == null) {
            throw new EventBusException("Event may not be null");
        } else if (postingState.event != event) {
            throw new EventBusException("Only the currently handled event may be aborted");
        } else if (postingState.subscription.subscriberMethod.threadMode != ThreadMode.POSTING) {
            throw new EventBusException(" event handlers may only abort the incoming event");
        }

        //更改 canceled 为true
        postingState.canceled = true;
    }

其实只是将 postingState.canceled 标志位 设置为 true,生效的地方参见 【==发送消息 及 接受消息 中1.2 postSingleEventForEventType==】

五. 黏性事件

主要作用

添加一个黏性事件,用于解决异步的一些问题

源码梳理

1 HandlerPoster.postSticky
    public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        // 先触发一次
        post(event);
    }

发送黏性时间后 会先将事件保存到 stickyEvents 中,然后立马触发一次。当以后有黏性 订阅方法订阅后 会立即执行这个黏性事件,这也是为啥 黏性事件可以先发送再注册 也能接收到的原理。==黏性事件触发位置参考 【注册 及 注册订阅方法中1.2 EventBus.subscribe】==

六. 线程切换

主要作用

通过不同的 ThreadMode 指定 线程

源码梳理

【参考 发送消息 及 接受消息 中 1.3 EventBus.postToSubscription】
  • POSTING :默认值,那个线程发送消息就是那个线程接受消息
  • MAIN:指定主线程接受消息
  • MAIN_ORDERED:也是指定主线程接受消息,不过如果前一个也是main_ordered 则需要等前一个执行完成后才执行
  • BACKGROUND:指定后台线程(只有一个 ),处理如保存到数据库等操作。
  • ASYNC:接受是始终重启进程进行操作,一般是用于比较消耗时间的任务

总结

流程分析

  1. 获取实例步骤 过程中我们可以对EventBus做一些配置
  2. 订阅 步骤只是对订阅者及订阅方法映射的缓存,如果是黏性事件则立即触发 订阅方法
  3. 反订阅 步骤就是 删除订阅步骤缓存的映射
  4. 发送消息 及 接受消息 步骤就是: 通过订阅步骤中生成的缓存 查找到 对应方法并 对于不同的 ThreadMode ,做不同的线程切换,最后都调用对应的 订阅方法。
  5. 取消事件就相当于 停止 下一个 订阅方法的执行。

ThreadMode的几种模式

  • POSTING :默认值,那个线程发送消息就是那个线程接受消息
  • MAIN:指定主线程接受消息
  • MAIN_ORDERED:也是指定主线程接受消息,不过如果前一个也是main_ordered 则需要等前一个执行完成后才执行
  • BACKGROUND:指定后台线程(只有一个 ),处理如保存到数据库等操作。
  • ASYNC:接受是始终重启进程进行操作,一般是用于比较消耗时间的任务

知识点

  1. ==其实 Java中也有观察者模式的实现(Observer,Observable),其工作原理和 EventBus很像,只不过 EventBus 使用起来更加方便而且具有线程切换等优点。==

  2. 在某些情况下,比如 Activity中 getDeclaredMethods 比 getMethods 快,因为 getDeclaredMethods 只获取自身的方法(public、protected、private),而getMethods 会向上查找 所有父类的方法(public)。

  3. 查找订阅方法重如果我们没有手动设置过 EventBusBuilder 的 subscriberInfoIndexes 那么就会通过反射获取 注册方法 列表, 所以我们再使用EventBus 的时候最好通过builder指定一下 subscriberInfoIndexes,毕竟 反射 是比较耗时的。代码如下

      EventBus  eventBus = EventBus.builder()
                .addIndex(new MyEventBusIndex())
                .installDefaultEventBus();
  1. SubscriberMethodFinder中 使用反射获取 订阅方法列表和 通过 订阅索引获取 订阅方法列表 本质的区别是前者是在运行期 才组织关系,而 后者是在编译器 就已经确定。所以使用后者会 提高 运行速度。

你可能感兴趣的:(EventBus 3.2.0 源码阅读)