美文网首页知识 | 解析程序员开源工具技巧
关于EventBus,从使用到解析(下)

关于EventBus,从使用到解析(下)

作者: Ruheng | 来源:发表于2017-01-25 16:54 被阅读644次

    本篇文章主要从EventBus的常用的方法入手,以分析源码的方式讲解其中的实现原理。

    基本使用

    注册订阅

    EventBus.getDefault().register(this);
    

    事件处理

    @Subscribe(threadMode = ThreadMode.MainThread)
        public void  onNewsEvent(NewsEvent event){
            String message = event.getMessage();
            mTv_message.setText(message);
        }
    

    发布事件

     EventBus.getDefault().post(new NewsEvent("我是来自SecondActivity的消息,你好!"));
    

    以上是EventBus的基本使用。我们先从getDefault说起。

    getDefault()

    static volatile EventBus defaultInstance;
    public static EventBus getDefault() {
            if (defaultInstance == null) {
                synchronized (EventBus.class) {
                    if (defaultInstance == null) {
                        defaultInstance = new EventBus();
                    }
                }
            }
            return defaultInstance;
        }
    

    通过上述代码可以得知,getDefault()中通过双检查锁(DCL)机制实现了EventBus的单例机制,获得了一个默认配置的EventBus对象。
    下面我们继续看register()方法。

    register()

    在了解register()之前,我们先要了解一下EventBus中的几个关键的成员变量。方便对下面内容的理解。

    /** Map<订阅事件, 订阅该事件的订阅者集合> */
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    
    /** Map<订阅者, 订阅事件集合> */
    private final Map<Object, List<Class<?>>> typesBySubscriber;
    
    /** Map<订阅事件类类型,订阅事件实例对象>. */
    private final Map<Class<?>, Object> stickyEvents;
    

    下面看具体的register()中执行的代码。

    public void register(Object subscriber) {
            //订阅者类型
            Class<?> subscriberClass = subscriber.getClass(); 
            //判断该类是不是匿名类,如果是匿名累要使用反射
            boolean forceReflection = subscriberClass.isAnonymousClass();
            //获取订阅者全部的响应函数信息(即上面的onNewsEvent()之类的方法)
            List<SubscriberMethod> subscriberMethods =
                    subscriberMethodFinder.findSubscriberMethods(subscriberClass, forceReflection);
            //循环每一个事件响应函数,执行 subscribe()方法,更新订阅相关信息
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    

    由此可见,register()第一步获取订阅者的类类型. 第二步,通过SubscriberMethodFinder类来解析订阅者类,获取所有的响应函数集合. 第三步,遍历订阅函数,执行 subscribe()方法,更新订阅相关信息。
    关于 subscriberMethodFinder这里就不介绍了。先跟着线索,继续看subscribe()方法。
    subscribe 函数分三步。

    第一步:

            //获取订阅的事件类型
            Class<?> eventType = subscriberMethod.eventType;
           //获取订阅该事件的订阅者集合
            CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
          //把通过register()订阅的订阅者包装成Subscription 对象
            Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
            //订阅者集合为空,创建新的集合,并把newSubscription 加入
            if (subscriptions == null) {
                subscriptions = new CopyOnWriteArrayList<Subscription>();
                subscriptionsByEventType.put(eventType, subscriptions);
            } else {
                //集合中已经有该订阅者,抛出异常。不能重复订阅
                if (subscriptions.contains(newSubscription)) {
                    throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                            + eventType);
                }
            }
           //把新的订阅者按照优先级加入到订阅者集合中。
            synchronized (subscriptions) {
                int size = subscriptions.size();
                for (int i = 0; i <= size; i++) {
                    if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                        subscriptions.add(i, newSubscription);
                        break;
                    }
                }
            }
    

    第二步:

            //根据订阅者,获得该订阅者订阅的事件类型集合
            List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
           //如果事件类型集合为空,创建新的集合,并加入新订阅的事件类型。
            if (subscribedEvents == null) {
                subscribedEvents = new ArrayList<Class<?>>();
                typesBySubscriber.put(subscriber, subscribedEvents);
            }
           //如果事件类型集合不为空,加入新订阅的事件类型
            subscribedEvents.add(eventType);
    

    第三步:

    //该事件是stick=true。
    if (subscriberMethod.sticky) {
                //响应订阅事件的父类事件
                if (eventInheritance) {
                    Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                    //循环获得每个stickyEvent事件
                    for (Map.Entry<Class<?>, Object> entry : entries) {
                        Class<?> candidateEventType = entry.getKey();
                        //是该类的父类
                        if (eventType.isAssignableFrom(candidateEventType)) {
                             //该事件类型最新的事件发送给当前订阅者。
                            Object stickyEvent = entry.getValue();
                            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                        }
                    }
                } else {
                    Object stickyEvent = stickyEvents.get(eventType);
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
    

    由此可见,第一步:通过subscriptionsByEventType得到该事件类型所有订阅者信息队列,根据优先级将当前订阅者信息插入到订阅者队列subscriptionsByEventType中;
    第二步:在typesBySubscriber中得到当前订阅者订阅的所有事件队列,将此事件保存到队列typesBySubscriber中,用于后续取消订阅;
    第三步:检查这个事件是否是 Sticky 事件,如果是则从stickyEvents事件保存队列中取出该事件类型最后一个事件发送给当前订阅者。

    到此,便完成了订阅功能。下面是订阅的具体流程图:


    unregister()

    public synchronized void unregister(Object subscriber) {
       // 获取该订阅者所有的订阅事件类类型集合.
       List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
       if (subscribedTypes != null) {
           for (Class<?> eventType : subscribedTypes) {
               unsubscribeByEventType(subscriber, eventType);
           }
           // 从typesBySubscriber删除该<订阅者对象,订阅事件类类型集合>
           typesBySubscriber.remove(subscriber);
       } else {
           Log.e("EventBus", "Subscriber to unregister was not registered before: "
                   + subscriber.getClass());
       }
    }
    
    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 == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i --;
                    size --;
                }
            }
        }
    }
    

    unregister()方法比较简单,主要完成了subscriptionsByEventType以及typesBySubscriber两个集合的同步。

    post()

    public void post(Object event) {
        // 获取当前线程的Posting状态.
        PostingThreadState postingState = currentPostingThreadState.get();
        // 获取当前线程的事件队列.
        List<Object> eventQueue = postingState.eventQueue;
        //将当前事件添加到其事件队列
        eventQueue.add(event);
        //判断新加入的事件是否在分发中
        if (!postingState.isPosting) {
            postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                // 循环处理当前线程eventQueue中的每一个event对象.
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                // 处理完知乎重置postingState一些标识信息.
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }
    

    post 函数会首先得到当前线程的 post 信息PostingThreadState,其中包含事件队列,将当前事件添加到其事件队列中,然后循环调用 postSingleEvent 函数发布队列中的每个事件。

    private void postSingleEvent(Object event, PostingThreadState postingState) {
       //分发事件的类型
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
       //响应订阅事件的父类事件
        if (eventInheritance) {
            //找出当前订阅事件类类型eventClass的所有父类的类类型和其实现的接口的类类型
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h ++) {
                Class<?> clazz = eventTypes.get(h);
                //发布每个事件到每个订阅者
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
    ....................................
    }
    

    调用 postSingleEventForEventType 函数发布每个事件到每个订阅者。

    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState,
                                                Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            // 获取订阅事件类类型对应的订阅者信息集合.(register函数时构造的集合)
            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;
    }
    

    调用 postToSubscription 函数向每个订阅者发布事件。
    postToSubscription 函数中会判断订阅者的 ThreadMode,从而决定在什么 Mode 下执行事件响应函数。这里就不贴源码了。后续还牵着到反射以及线程调度问题,这里就不展开了。
    以上就是post的流程,下面是具体的post的流程图。


    参考文章:
    EventBusAnalysis
    EventBus 源码解析

    相关文章

      网友评论

        本文标题:关于EventBus,从使用到解析(下)

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