EventBus源碼解析(二)—發布事件和注銷流程

1.EventBus源碼解析(一)—訂閱過程
2.EventBus源碼解析(二)—發布事件和注銷流程
3.EventBus源碼解析(三)—進階源碼

前言

上一篇博客已經比較詳細的講解了EventBus的注冊過程,有了上一篇博客的基礎,其實關于EventBus的源碼中的其他流程就非常好理解了,尤其是我認為EventBus中最為重要的兩個Map,理解了兩張圖其實就理解了EventBus的原理。

源碼分析

1.發布事件

post方法

/** Posts the given event to the event bus. */
    public void post(Object event) {
        //ThreadLocal保存的,不同的線程互相不干擾
        PostingThreadState postingState = currentPostingThreadState.get();
        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;
            }
        }
    }

既然是發布事件,肯定是post方法。
這里首先要注意一點,這里的currentPostingThreadState是一個ThreadLocal類型,既然選擇TreadLocal類型,那么特點當然就是線程單例,不同的線程之間互相不干擾,因為我們知道EventBus是支持多線程之間的事件傳遞的。
接下來的源碼都比較簡單,將事件加入當前線程中用于保存事件的隊列中,然后遍歷隊列中的事件,執行postSingleEvent方法發送事件。

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);
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            //直接發送當前類
            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) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

可以看到這里的流程比較簡單,基本都是最后調用了postSingleEventForEventType方法,當然如果沒有找到,則會發送一條沒有找到的NoSubscriberEvent.
這里有個比較重要的參數eventInheritance,這個如果我們沒有特殊配置的話,默認是true,我們來看一下這個參數是干什么的,可以看到當這個參數為true的時候,會調用lookupAllEventTypes方法,返回一個List集合。

private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
        synchronized (eventTypesCache) {
            List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
            if (eventTypes == null) {
                eventTypes = new ArrayList<>();
                Class<?> clazz = eventClass;
                while (clazz != null) {
                    //加入當前類
                    eventTypes.add(clazz);
                    //加入當前類的所有接口
                    addInterfaces(eventTypes, clazz.getInterfaces());
                    //加入當前的父類
                    clazz = clazz.getSuperclass();
                }
                eventTypesCache.put(eventClass, eventTypes);
            }
            return eventTypes;
        }
    }

可以看到,這個方法的所用就是用一個List保存該Event類型自己本身,和其繼承的所有父類,和實現的所有接口。
所以當得到這個List集合后,會遍歷這個集合,調用postSingleEventForEventType發送事件,所以這里可以看出默認情況下不要隨意將Event繼承或者實現接口,當發送該Event的時候,都會一并發出。

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

這時就可以看到我們前一篇博客提到的非常重要的一個Map:subscriptionsByEventType,這里放上這個Map的圖便于理解。

subscriptionsByEventType

所以我們通過Event的類型,找到了所有訂閱該Event的信息CopyOnWriteArrayList<Subscription>。接下來就是遍歷這個List調用postToSubscription方法,利用反射來調用訂閱的方法。

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            //默認類型
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) {
                    //如果當前就在UI線程,則直接反射執行
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED:
                //不同于MAIN,直接通過Handler的隊列執行,串行的
                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) {
                    //如果當前是UI線程,則異步
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    //不是UI線程,則在該線程執行
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

可以看到這里實現了EventBus中的多線程功能,具體實現本篇博客就不做講解了,后面如果有時間,會講解一下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);
        }
    }

果然不出所料,最后就是通過我們經常看到的反射里調用訂閱者中的訂閱方法,來實現EventBus中的事件流程。

2.注銷流程

unregister

public synchronized void unregister(Object subscriber) {
        //找到訂閱者訂閱的所有事件類型,也就是MainActivity中所有的Event
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                //到Event的map中刪除MainActivity
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

可以看到這里又用到我們所說的重要的Map中的其中一個typesBySubscriber,這里再放一下這個Map的圖便與我們理解。

typesBySubscriber

可以看到這里,從typesBySubscribermap中取出當前訂閱者的訂閱的所有事件集合List,然后再遍歷這個List,調用unsubscribeByEventType方法進行取消訂閱,其實很容易聯想到這個方法肯定是再到另一個重要的Map中查找訂閱了這個Event的訂閱者中移除當前Activity的訂閱者。

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

果然不出所料,又看到我們熟悉的另一個MapsubscriptionsByEventType,這里遍歷該Activity中訂閱的所有事件,然后通過Event到subscriptionsByEventType中查找訂閱了該Event的List<Subscription>集合,然后找到當前這個訂閱者(注意這里使用的是==,來判斷兩個Object對象是否相等,意味著是內存地址相同),找到后移除。至此注銷流程也到此結束。

3.總結

通過本篇博客,EventBus的基本流程基本上我們已經有了一定程度上的理解,詳細閱讀了這兩篇博客后對于第一篇博客中提出的問題也能相應理解。而且更重要的貫徹EventBus整個流程的兩個重要的Map。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容