EventBus 3.0 使用詳解源碼分析(三)

目錄

前言

Post流程

總結


  • 前言

            前面的文章已經寫完了註冊流程,現在開始寫post流程,如果沒有看過的讀者,可以通過eventbus註冊流程傳送。

  • Post流程

           首先EventBus#getDefault()獲取實例,而在這裏還有一個初始化的方法,通過EventBusBuilder來實現初始化:

 public EventBus build() {
        return new EventBus(this);
    }

         既然EventBus是一個模仿單例的模式設計的,爲什麼又會存在public的構造函數,這是因爲eventbus這樣設計,可以同時開始多條事件總線而不會彼此影響。因此開發的時候也要注意,不能一會通過builder構造,一個用getDefault()獲取,如果不弄清楚可能會造成訂閱者無法處理髮布消息的情況。

         接着來看Event#post(Object o)函數的流程:

/** Posts the given event to the event bus. */
    public void post(Object event) {
        //currentPostingThreadState是一個本地線程,一定要了解本地線程的機制,因爲本地線程
        // 通過get操作獲取不同線程的PostingThreadState
        //PostingThreadState這個內部類描述了發佈者線程屬性信息,訂閱事件對象,以及觀察者信息,
        PostingThreadState postingState = currentPostingThreadState.get();
        //獲取postingThreadState中保存訂閱消息的列表,並將此事件保存。
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        //isposting 默認false
        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;
            }
        }
    }

    這裏要了解PostingThreadState這個內部類。它描述了發佈,訂閱,事件三者的信息。按照順序接下來要看BustEvent#postSingleEvent函數:

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        //獲取發佈事件的類型
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        //默認時候true
        if (eventInheritance) {
            //將訂閱事件的類型的父類和實現的接口都檢索出來。
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            //遍歷eventypes將所有事件發佈出去,因此註冊了該事件父類的觀察者也會處理相應的消息。
            //比如MessageObject 父類是MessageA, 那麼發佈MessageObject消息的時候,也會把MessageA發佈出去。
            //因此相關的觀察者也會調用。
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                //通過postSingleEventForEventType讓訂閱者實現訂閱事件的處理。
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            //通過builder初始化EventBus設置eventInheritance爲false就不會再檢索父類,這樣可以提高效率。
            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));
            }
        }
    }

        通過lookupAllEventTypes()獲取消息類的父類和實現的接口,這個要看我們的使用需求。是否需要發送消息的時候將父類也一同post然後可以通過配置來確認。按順序執行EventBus#postSignleEventForEventType()

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            //前文提到過subscriptionsByEventType保存了訂閱事件和訂閱者的信息,在這裏根據事件類型獲取所有訂閱者信息。
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) {
                //遍歷所有的觀察者,通過postToSubscription函數來實現觀察者方法的反射執行
                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;
    }

       在這裏就是根據訂閱事件類在SubscriptionByEventType這個map裏面檢索出所有的觀察者,因爲在前文中遍歷了訂閱事件的父類,所以這裏會多次調用。將訂閱事件的父類的所有觀察者也會檢索出來依次分發事件。之後在EventBus#postToSubscription裏面執行最終的邏輯反射調用。


    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        //在這裏通過不同的線程屬性來實現不同的調用方法。最終都是通過invokeSubscriber()來實現
        //invokeSubscriber()函數就是單純的反射來實現。
        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);
        }
    }

         在這裏根據註冊者中註冊方法註解裏面的線程模式來進行不同的分發。首先如果是Posting模式,那麼直接通過反射執行事件處理,典型的從哪裏來到哪裏去,這也是默認的方式。然後是Mian模式,如果事件是ui線程發出的,那麼直接在ui線程處理。如果是線程發出的,那麼通過mainposter會把時間發送到ui線程。如果是Background模式,如果事件是在ui線程發出的,那麼會重新開一個線程執行,否則就直接執行,還是從哪裏來,到哪裏去。如果是async模式,直接開一個線程就ok。這裏需要說的是mainPoster,backgroundPoster和asyncposter三個分發處理。如果是主線程發出的在MAIN這種模式下。直接通過調用invokeSubScriber()直接通過反射就執行了。遵從從哪裏來到哪裏去的原則。如果是從線程裏面發出的事件,需要在ui線程處理。那麼就需要將事件post到ui線程,可以想想應該用到了handler。

public class HandlerPoster extends Handler implements Poster {

    private final PendingPostQueue queue;
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    private boolean handlerActive;

    protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
        super(looper);
        this.eventBus = eventBus;
        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

    @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            while (true) {
                PendingPost pendingPost = queue.poll();
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;
                            return;
                        }
                    }
                }
                eventBus.invokeSubscriber(pendingPost);
                long timeInMethod = SystemClock.uptimeMillis() - started;
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }
}

       MainPoster就是這個HandlerPoster,首先繼承hander,關於handler無需多講,再看實現了接口Poster,各個類型的poster都實現了這個接口,這個接口也只有一個方法enqueue(),它主要實現了將post事件信息添加到鏈表中。而mainPoster也主要是將各個事件通過handler發佈出去。然後也是通過invokeSubscriber來實現。當處理一個事件的時候,其他事件等候,順序執行。這裏面涉及到PendingPostQueue, 他是一個類似隊列的數據機構模式,提供入隊和出隊的操作,沒有什麼複雜的地方,大家可以自己看看。需要說的是隊列中的元素類型PendingPost.

final class PendingPost {
    private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();

    Object event;
    Subscription subscription;
    PendingPost next;

    private PendingPost(Object event, Subscription subscription) {
        this.event = event;
        this.subscription = subscription;
    }

    static PendingPost obtainPendingPost(Subscription subscription, Object event) {
        synchronized (pendingPostPool) {
            int size = pendingPostPool.size();
            if (size > 0) {
                PendingPost pendingPost = pendingPostPool.remove(size - 1);
                pendingPost.event = event;
                pendingPost.subscription = subscription;
                pendingPost.next = null;
                return pendingPost;
            }
        }
        return new PendingPost(event, subscription);
    }

    static void releasePendingPost(PendingPost pendingPost) {
        pendingPost.event = null;
        pendingPost.subscription = null;
        pendingPost.next = null;
        synchronized (pendingPostPool) {
            // Don't let the pool grow indefinitely
            if (pendingPostPool.size() < 10000) {
                pendingPostPool.add(pendingPost);
            }
        }
    }

}

      PendingPost實現了一個對象池的概念,final類,然後構造函數是私有的。 從而在線程上也是安全的。在這裏只能獲取PendingPost對象,之後不用的時候釋放。 從而不會使內存爆發式增長。 其實eventbus還有其他地方也涉及到對象池的概念。 大家自己分析的時候會看到。就不一一舉例了。之後我們看BackGroundPoster

final class BackgroundPoster implements Runnable, Poster {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    private volatile boolean executorRunning;

    BackgroundPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!executorRunning) {
                executorRunning = true;
                eventBus.getExecutorService().execute(this);
            }
        }
    }

    @Override
    public void run() {
        try {
            try {
                while (true) {
                    PendingPost pendingPost = queue.poll(1000);
                    if (pendingPost == null) {
                        synchronized (this) {
                            // Check again, this time in synchronized
                            pendingPost = queue.poll();
                            if (pendingPost == null) {
                                executorRunning = false;
                                return;
                            }
                        }
                    }
                    eventBus.invokeSubscriber(pendingPost);
                }
            } catch (InterruptedException e) {
                eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
    }

}

       BackgroundPoster這裏實現了Runnable,通過這樣來實現在新的線程處理事件, 其中通過if(!executorRunning)可以看出添加的是時候當一個事件執行的時候,其他事件只是會被添加到隊列中去,但是不會被立刻執行,所以在run中有一個while循環來不停的獲取隊列中的事件去執行。因此可以看出,Background模式不宜執行太耗時的操作。接下來看AsyncPoster,

class AsyncPoster implements Runnable, Poster {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    AsyncPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        queue.enqueue(pendingPost);
        eventBus.getExecutorService().execute(this);
    }

    @Override
    public void run() {
        PendingPost pendingPost = queue.poll();
        if(pendingPost == null) {
            throw new IllegalStateException("No pending post available");
        }
        eventBus.invokeSubscriber(pendingPost);
    }

}

     可以看出,Asyncposter也實現了Runnable,但是他就是直接new一個線程就不管了。所以可以在這個模式下執行耗時操作。到這裏post的流程基本結束了。大致可見post流程也可以分爲三個流程:

           1 通過事件將觀察者檢索出來。

           2 根據ThreadMode模式將分發事件的方式確定。

           3 通過反射處理事件。

  • 總結

          大致跟隨源碼講了一下eventbus的流程,其實還有很多需要注意的地方,比如黏性和優先權。 之後會在剩下的博客裏說一說。

 

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