EventBus原始碼分析(一):入口函式提綱挈領(2.4版本)

王世暉發表於2016-07-01

EventBus原始碼分析(一):入口函式提綱挈領(2.4版本)
EventBus原始碼分析(二):register方法儲存事件的訂閱者列表(2.4版本)
EventBus原始碼分析(三):post方法釋出事件【獲取事件的所有訂閱者,反射呼叫訂閱者事件處理方法】(2.4版本)
EventBus原始碼分析(四):執行緒模型分析(2.4版本)
EventBus原始碼解讀詳細註釋(1)register的幕後黑手
EventBus原始碼解讀詳細註釋(2)MainThread執行緒模型分析
EventBus原始碼解讀詳細註釋(3)PostThread、MainThread、BackgroundThread、Async四種執行緒模式的區別
EventBus原始碼解讀詳細註釋(4)register時重新整理的兩個map
EventBus原始碼解讀詳細註釋(5)事件訊息繼承性分析 eventInheritance含義
EventBus原始碼解讀詳細註釋(6)從事件釋出到事件處理,究竟發生了什麼!

本文是EventBus原始碼分析的第一篇文章,不拘泥於具體的實現細節,從巨集觀上把握EventBus的設計思路,而EventBus總體的設計思路為:
EventBus例項儲存了事件到訂閱者列表的map,釋出事件的時候,從該map中取出該事件的所有訂閱者,在規定的執行緒中反射呼叫所有訂閱者的事件處理方法。

DCL單例建立EventBus物件

EventBus提供了靜態方法建立EventBus物件,實現方式為標準的DCL雙檢鎖的單例模式。DCL雙檢鎖分為兩部分:

  1. 靜態易變的defaultInstance屬性
  2. 靜態的getDefault方法
    ...
    static volatile EventBus defaultInstance;
    ...
    /** Convenience singleton for apps using a process-wide EventBus instance. */
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

但是EventBus並非嚴格的DCL單例模式,因為構造器並不是私有的,因此並不能保證EventBus例項的唯一性。不同例項之間是匯流排隔離的,即EventBus例項A釋出的事件event並不能被EventBus例項B訂閱接受。

register方法向EventBus註冊訂閱者

register(this)就是在當前類(Activity等)遍歷所有方法,按照EventBus事件處理方法的命名規則約束(onEvent開頭,只有一個引數,非static,非abstract的public方法)過濾出事件處理方法然後進行儲存。
因此register主要分為兩步:

  1. 找到訂閱者訂閱的所有事件
  2. 對每個訂閱者訂閱的所有事件,逐一進行訂閱
    private synchronized void register(Object subscriber, boolean sticky, int priority) {
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod, sticky, priority);
        }
   `
}

EventBus針對每一個事件,維護了一個訂閱了這個事件的訂閱者的列表,並且用HashMap來儲存,HashMap的鍵為事件的Class,值為該事件的訂閱者列表。

    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;

該HashMap有兩點需要注意:

  1. 該HashMap是物件屬性不是類屬性,因此不同的EventBus例項的該屬性是不同的,這也是不同EventBus例項之間匯流排隔離的原因
  2. 訂閱者列表並沒有通過ArrayList實現,為了達到執行緒併發安全,採用CopyOnWriteArrayList

post方法向EventBus釋出事件

EventBus儲存了事件到及該事件的訂閱者列表,因此釋出事件就應該是找到該事件的訂閱者列表,對每一個訂閱者通過反射呼叫事件處理方法。

    /** Posts the given event to the event bus. */
    public void post(Object event) {
        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 {
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

post方法首先獲取了事件釋出者的執行緒狀態,從該執行緒狀態中獲取事件佇列,然後不停得處理佇列中的事件,直到佇列為空

         while (!eventQueue.isEmpty()) {
             postSingleEvent(eventQueue.remove(0), postingState);
         }

postSingleEvent方法裡邊進一步呼叫了postSingleEventForEventType方法,找到該事件的訂閱者列表

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

首先在同步程式碼塊中獲取該事件的訂閱者列表

        synchronized (this) {
            subscriptions = subscriptionsByEventType.get(eventClass);
        }

然後遍歷訂閱者列表,對每一個訂閱者進行處理

 for (Subscription subscription : subscriptions) {
    ...
    postToSubscription(subscription, event, postingState.isMainThread);
    ...
 }

而postToSubscription方法就是根據不同的執行緒模式,在不同的執行緒中反射呼叫訂閱者的事件處理方法。

    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case PostThread:
                invokeSubscriber(subscription, event);
                break;
            case MainThread:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case BackgroundThread:
                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);
        }
    }

unregister方法-訂閱者向EventBus解註冊

為了防止記憶體洩露,像Android中的廣播或者其他的訂閱者模式,有註冊就有解註冊,EventBus也不例外。unregister方法一般在與呼叫register方法對稱的生存週期回撥中呼叫。

    /** Unregisters the given subscriber from all event classes. */
    public synchronized void unregister(Object subscriber) {
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                unubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

在register註冊的時候,儲存了兩個重要的HashMap,其中一個就是上文提到的事件到訂閱者列表的map,該map用於釋出事件的時候尋找該事件的訂閱者列表,然後通過反射呼叫訂閱者的事件處理方法。第二個就是訂閱者到事件列表的map,表明了該訂閱者訂閱了哪些事件,主要用於解註冊。

    private final Map<Object, List<Class<?>>> typesBySubscriber;

unregister方法先從此map中找到該訂閱者訂閱的所有事件

   List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);

然後對每一個事件進行解註冊

    for (Class<?> eventType : subscribedTypes) {
        unubscribeByEventType(subscriber, eventType);
    }

最後從此map中刪除該訂閱者

   typesBySubscriber.remove(subscriber);

本文結束,接下來會深入原始碼,從細節上探索EventBus的設計思路。

相關文章