美文网首页
Eventbus 3.1.1 源码解析(二)

Eventbus 3.1.1 源码解析(二)

作者: LouisXWB | 来源:发表于2019-03-10 22:50 被阅读0次

    接着上篇文章,继续对Eventbus的源码进行解析

    3.3 EventBus post(Object subscriber)

    在介绍基本使用时,我们已经知道了发送事件是使用post(Object subscriber),接下来我们来分析一下它的源码:

    /** Posts the given event to the event bus. */
        public void post(Object event) {
            PostingThreadState postingState = currentPostingThreadState.get();
            //得到当前线程的postingState
            List<Object> eventQueue = postingState.eventQueue;
            //获取当前线程的事件队列
            eventQueue.add(event);
            //把发送事件加入到事件队列中
    
            if (!postingState.isPosting) {
                postingState.isMainThread = isMainThread();
                postingState.isPosting = true;
                if (postingState.canceled) {
                    throw new EventBusException("Internal error. Abort state was not reset");
                }
                try {
                    //只要队列不为空,则一直发送事件
                    while (!eventQueue.isEmpty()) {
                        postSingleEvent(eventQueue.remove(0), postingState);
                    }
                } finally {
                    postingState.isPosting = false;
                    postingState.isMainThread = false;
                }
            }
        }
    

    通过ThreadLocal<PostingThreadState> currentPostingThreadState获取当前的postingState,关于ThreadLocal,它是一个线程内数据存储类,它的作用域只在线程内部,支持在指定线程内进行存储和读取数据,Android消息机制中,MessageQueue就是使用ThreadLocal来存储数据。

    因此,通过获取到的postingState就是当前线程保存的部分数据,参数如下:

     /** For ThreadLocal, much faster to set (and get multiple values). */
        final static class PostingThreadState {
            final List<Object> eventQueue = new ArrayList<>();
            boolean isPosting;
            boolean isMainThread;
            Subscription subscription;
            Object event;
            boolean canceled;
        }
    

    接着,我们来看下发送单个事件的方法postSingleEvent(eventQueue.remove(0), postingState) 是如何实现的:

    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
            //得到事件的类型
            Class<?> eventClass = event.getClass();
            //初始化是否找到订阅者
            boolean subscriptionFound = false;
            
            //如果支持事件继承,则找到所有的父类事件和接口并发送
            if (eventInheritance) {
                List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
                int countTypes = eventTypes.size();
                for (int h = 0; h < countTypes; h++) {
                    Class<?> clazz = eventTypes.get(h);
                     //下面这句代码的理解是,只要有一个返回true,subscriptionFound就为true
                    subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
                }
            } else {
                subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
            }
            
            //如果没有找到订阅者,则根据初始的配置,判断是否进行日志信息打印和NoSubscriberEvent事件发送
            if (!subscriptionFound) {
                if (logNoSubscriberMessages) {
                    logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
                }
                if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                        eventClass != SubscriberExceptionEvent.class) {
                    post(new NoSubscriberEvent(this, event));
                }
            }
        }
    

    接着我们继续看下发送方法postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass)的实现:

    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
            CopyOnWriteArrayList<Subscription> subscriptions;
            synchronized (this) {
                // 获取事件的所有订阅者
                subscriptions = subscriptionsByEventType.get(eventClass);
            }
            if (subscriptions != null && !subscriptions.isEmpty()) {
                //遍历所有的订阅者
                for (Subscription subscription : subscriptions) {
                    postingState.event = event;
                    postingState.subscription = subscription;
                    boolean aborted = false;
                    try {
                        //发送事件至订阅者
                        postToSubscription(subscription, event, postingState.isMainThread);
                        aborted = postingState.canceled;
                    } finally {
                        postingState.event = null;
                        postingState.subscription = null;
                        postingState.canceled = false;
                    }
                    if (aborted) {
                        break;
                    }
                }
                return true;
            }
            return false;
        }
    

    上面的方法就是通过找到事件的订阅者,然后向每个订阅者进行事件发送,如果成功发送则返回true,没有找到订阅者或者事件被取消则返回false。

    接着,我们继续看下事件是如何传递给订阅者的,

    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
            switch (subscription.subscriberMethod.threadMode) {
                case POSTING:
                    invokeSubscriber(subscription, event);
                    break;
                case MAIN:
                    if (isMainThread) {
                        invokeSubscriber(subscription, event);
                    } else {
                        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、POSTING
      订阅事件与发送事件在同一线程。对线程无限制,但是如果发送post线程为主线程,则订阅方法不能有耗时的操作,适用于耗时少、对线程无要求的场景;
    • 2、 MAIN
      必须在主线程执行。如果当前线程是主线程,则直接执行订阅方法,如果不是,则通过Hanlder发送消息到主线程,再执行订阅方法,适用于UI绘制或者耗时少等必须在主线程执行的场景。
    • 3、 MAIN_ORDERED
      跟MAIN一样,都是在主线程执行。区别在于,它没有先判断发送事件的线程是否为主线程,直接通过Hanlder发送消息再调用主线程执行订阅方法。
    • 4、 BACKGROUND
      在后台线程执行。如果发送事件的线程在主线程,调用子线程去执行,否则直接在当前线程执行订阅方法。这与直接通过异步线程去执行的区别就是,后台线程backgroundPoster只有一个,如果事件过多,backgroundPoster里面维护了一个发布队列,依次执行事件,但这也导致了执行过程中,其他订阅方法的响应会一直处于等待中的状态。因此,如果方法执行耗时、执行过于频繁,都会对其他订阅方法的执行造成影响。所以这种模式适用于轻微耗时、执行不会过于频繁的场景。
    • 5、 ASYNC
      异步执行。直接调用异步线程执行,不存在其他方法的执行处于等待的现象,适用于耗时操作。

    最后,我们看下启动订阅方法执行的invokeSubscriber(Subscription subscription, Object event)

    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);
            }
        }
    

    通过反射找到订阅者方法,然后传入对应的事件执行订阅逻辑,到此,发送事件的流程也解析完毕,整体流程如下:


    post-flow-chart.png

    3.4 unregister(Object subscriber)

    最后,我们看看整个流程的最后一步,解除注册unregister(Object subscriber)的解析:

     /** Unregisters the given subscriber from all event classes. */
        public synchronized void unregister(Object subscriber) {
            List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
            //通过订阅者找到订阅的事件类型list
            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());
            }
        }
    

    具体移除订阅者和事件关联的逻辑如下:

    /** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
        private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
            List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
            if (subscriptions != null) {
                int size = subscriptions.size();
                for (int i = 0; i < size; i++) {
                    Subscription subscription = subscriptions.get(i);
                    if (subscription.subscriber == s ubscriber) {
                        subscription.active = false;
                        subscriptions.remove(i);
                        i--;
                        size--;
                    }
                }
            }
        }
    

    上面的解除注册方法就是找到订阅者订阅的所有事件,依次遍历,移除每个事件与订阅者的关联,最后把订阅者对应订阅事件的缓存也清除,防止内存泄露的发生。

    至此,Eventbus的源码已经分析得差不多,后面对分析它整体的设计,思考我们可以学习和借鉴的地方。

    相关文章

      网友评论

          本文标题:Eventbus 3.1.1 源码解析(二)

          本文链接:https://www.haomeiwen.com/subject/dhtzuqtx.html