EventBus源碼解析之消息發送

上一篇我們講了eventBus 的使用和註冊 EventBus使用及源碼解析之註冊   

建議在看消息發送的時候先看上一篇,這篇講到的有些東西在上邊提過的我就不再多說了。

 消息的發送有兩種方法,post和postSticky,其實postSticky 是對post的又一次包裝,不過是postSticky 多了一步操作,它將要發送的消息重新緩存到了stickyEvent中了,操作完成後還是調用了post 方法。

EventBus.getDefault().post("普通事件");

EventBus.getDefault().postSticky("粘性事件");

 public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        // Should be posted after it is putted, in case the subscriber wants to remove immediately
        post(event);
    }

我們在上邊註冊的時候有看到,在最後我們遍歷了stickyEvents map ,它的數據存儲入口就是這裏,並且它存完數據後又執行了post方法。

public void post(Object event) {
       // PostingThreadState 是消息隊列工具類,並且是線程安全的,他是這個隊列封裝類變量的的控制器,且每個線程只有一個PostingThreadState 對象
        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 循環,逐個發送任務
                while (!eventQueue.isEmpty()) {
                    //開始發送,獲取第一個元素並刪除隊列中第一個元素,
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                //執行完成後將隊列初始化
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

postSingleEvent 是對單個任務的發送,

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        //獲取發送任務消息的class 
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        if (eventInheritance) {
           //獲取訂閱函數參數類型的class集合
            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的三個參數分別是1.event消息實例 2.隊列控制封裝類 3.訂閱方法的參數類型

其中3的參數我們可以拿到訂閱方法的所有信息的封裝類 Subscription集合,上篇專門講過。

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
       //subscriptionsByEventType  對象就是我們註冊時保存訂閱方法的對象,它的鍵時訂閱方法的參數的classe類型,每個classs類型歸位一類,他返回的事一個CopyOnWriteArrayList<Subscription> 集合。是訂閱方法參數類型一致的訂閱的方法的集合。
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
       //Subscription 是單個訂閱方法的封裝類
        for (Subscription subscription : subscriptions) {
            //將消息內容及訂閱方法封裝類負值給postingState
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted;
            try {
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } finally {
                //執行完成後初始化postingState 後邊消息還會複用
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            //如果這時隊列類被標記爲取消,那麼就推出循環訂閱
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
}

我們看到現在調用到了postToSubscription 方法,這個方法我們在上一篇中也講過,主要用於判斷執行在那個線程中。然後執行

invokeSubscriber 方法使用反射將消息傳遞給訂閱方法,同時訂閱方法被執行。

 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);
        }
    }
最終執行
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);
    }
}

然後我麼的訂閱方法執行

 //訂閱事件,並且當前方法是在主線程中被調用的,sticky表示是否粘性事件
    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void onEventTest(Object msg){
        Log.d(TAG, "onEvent: " + msg.toString());
        EventBus.getDefault().removeStickyEvent(msg);
    }

如果你的事件是一個粘性訂閱事件,當收到的消息也是一個粘性消息時,如果後邊沒有其他需求,需要將該消息從粘性消息map 中移除,否則後邊如果有新註冊的粘性訂閱方法還會收到此消息。

總結:整個消息發送的過程實際就是消息重組和消息入列和出列的過程,在發送的時候無論是哪種發送方法,凡事已經註冊和並訂閱的方法都會被調用到,除非你手動設置了隊列的停止和刪除動作。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章