EventBus原始碼解讀詳細註釋(4)register時重新整理的兩個map

王世暉發表於2016-03-20

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



EventBus中有兩個重要的map,一個是事件到訂閱者列表的map,另一個是訂閱者到事件列表的map,每次register的時候都要動態重新整理這兩個map的資料。正是因為有了這兩個map,EventBus在post事件的時候才可以根據事件型別找到所有訂閱了此事件的訂閱者,然後使用反射呼叫訂閱者的事件處理方法。

先看看EventBus中這兩個map是如何定義的

/*事件到訂閱者列表的map,key是事件,也就是訊息處理方法的引數的Class,value是所有的訂閱此事件的訂閱者列表*/
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
/*訂閱者到訂閱者訂閱的所有事件列表的map,key是訂閱者,value是該訂閱者訂閱的所有事件的列表*/
private final Map<Object, List<Class<?>>> typesBySubscriber;
在register的時候會動態的重新整理這兩個map,主要是重新整理map的value即重新整理列表。

private synchronized void register(Object subscriber, boolean sticky, int priority) {
        /*getClass():* Returns the unique instance of  Class that represents this object's class.*/
        /*過濾出訂閱者的所有事件處理方法(包括父類的),以列表形式返回*/
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            /*遍歷,對訂閱者的每一個事件處理方法進行訂閱*/
            subscribe(subscriber, subscriberMethod, sticky, priority);
        }
    }

subscribe方法中主要做了一件事,就是更新那兩個重要的map

 /**
     * @param subscriber 訂閱者
     * @param subscriberMethod 訂閱者的事件處理方法
     * @param sticky 是否是粘性事件
     * @param priority 優先順序
     */
    // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
        /*每次subscribe都要更新兩個map,事件到訂閱者列表的map,和訂閱者到事件列表的map*/
        /*下邊的程式碼先更新事件到訂閱者列表的map,主要是更新此事件的訂閱者列表*/
        /*獲取訊息型別,也就是事件處理方法的引數型別*/
        Class<?> eventType = subscriberMethod.eventType;
        /*private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
        * subscriptionsByEventType: key:訊息型別(事件處理方法引數型別),value:接受此訊息型別的所有訂閱者列表
        * */
        /*獲取所有接受此訊息型別的訂閱者列表,然後把新的訂閱者加入此列表*/
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        /*根據訂閱者,事件、優先順序封裝一個新的訂閱者封裝類Subscription*/
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
        /*如果此訊息型別的訂閱者列表為null,表示還沒有訂閱者訂閱此型別訊息,就建立一個訂閱者列表*/
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<Subscription>();
            /*將事件和事件對應的訂閱者列表加入全域性的map*/
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            /*如果接收此訊息型別的訂閱者列表不為空,並且列表裡邊已經有此訂閱者,丟擲重複註冊異常*/
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

        // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
        // subscriberMethod.method.setAccessible(true);
        /*更新事件的訂閱者列表*/
        /*將新的註冊的訂閱者根據優先順序插入到合適位置,priority表示優先順序,按照該數值從大到小排列*/
        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {
            /*按照優先順序遍歷 逐個比較*/
            if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
                /*注意寫時複製的思想,CopyOnWriteArrayList增加資料的原始碼如下所示*/
                /*
                public synchronized void add(int index, E e) {
                    Object[] newElements = new Object[elements.length + 1];

                   public static native void arraycopy(Object src, int srcPos,Object dst, int dstPos, int length);
                     * Copies length elements from the array src,
                     * starting at offset srcPos, into the array dst,
                     * starting at offset dstPos.
                     * @param src the source array to copy the content.
                     * @param srcPos  the starting index of the content in {@code src}.
                     * @param dst the destination array to copy the data into.
                     * @param dstPos the starting index for the copied content in {@code dst}.
                     * @param length the number of elements to be copied.

                    System.arraycopy(elements, 0, newElements, 0, index);
                    newElements[index] = e;
                    System.arraycopy(elements, index, newElements, index + 1, elements.length - index);
                    elements = newElements;
                 }
                 */
                /*CopyOnWriteArrayList並不是按照資料結構裡邊將的那樣通過資料移位來新增元素,那樣效率太低了
                * 而是先建立一個比原來列表size大1的新列表,將新新增資料前邊的所有資料整體賦值到新列表前邊,
                * 將要新增的新數新增在新列表的後邊,然後把剩下的一起復制到新列表*/
                subscriptions.add(i, newSubscription);
                break;
            }
        }
        /*然後接下來更新訂閱者到事件列表的map,主要是更新事件列表*/
        /*private final Map<Object, List<Class<?>>> typesBySubscriber;
        * typesBySubscriber :key訂閱者 value訂閱者訂閱的訊息列表
        * typesBySubscriber是EventBus的成員屬性,表示一個map,key是訂閱者,value是該訂閱者訂閱的所有事件的列表,
        * 是全域性的,要囊括所有訂閱者
        * subscriptionsByEventType也是EventBus的成員屬性,表示一個map,key是事件,value是訂閱了改事件的所有訂閱者的列表,
        * 是全域性的,要囊括所有事件*/
        /*獲取訂閱者的接受的所有訊息型別列表*/
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        /*如果此訂閱者還沒有以前沒有訂閱事件,則建立列表,新增事件*/
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<Class<?>>();
            /*typesBySubscriber是EventBus的類成員*/
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        /*將此事件新增到訂閱者訂閱的事件的列表,更新事件列表*/
        subscribedEvents.add(eventType);
        /*處理粘性事件*/
        if (sticky) {
            /*是否要考慮事件的繼承性*/
            if (eventInheritance) {
                // Existing sticky events of all subclasses of eventType have to be considered.
                // Note: Iterating over all events may be inefficient with lots of sticky events,
                // thus data structure should be changed to allow a more efficient lookup
                // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                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);
            }
        }
    }


相關文章