源碼解讀系列(二)EventBus3.1.1

1、EventBus的構造過程

使用EventBus的時候,首先要獲取EventBus
eventbus
EventBus
構造方法是一個雙重檢查的單例模式。
EventBus
調用了 EventBus(DEFAULT_BUILDER);傳入了一個默認的EventBusBuilder
EventBus
可以看到使用了EventBusBuilder來對EventBus進行了各種參數的配置

2、EventBus的訂閱過程

EventBus
EventBus
subscriberMethodFinder.findSubscriberMethods(subscriberClass);方法找出一個SubscriberMethod的集合,這個集合就是所有傳進來的訂閱方法,接着遍歷所有訂閱者的訂閱方法使用 subscribe(subscriber, subscriberMethod);來進行註冊。
很明顯註冊方法完成了兩件事:

1、查找所有訂閱者的訂閱方法
2、完成所有訂閱者的註冊

我們看看SubscriberMethod類
EventBus
原來這個類是用來保存訂閱者的Method對象,線程模式,事件類,優先級,是否是黏性事件等基本參數。
至於methodString字符串是使用method對象和eventType對象構造的一個字符串,用於public boolean equals(Object other)方法,可以更加嚴謹正確的判斷是否是同一個對象。

如何查找所有訂閱者的訂閱方法的

subscriberMethodFinder.findSubscriberMethods(subscriberClass);
EventBus
首先從緩存裏獲取所有的訂閱者方法信息,如果獲取到就直接返回,沒有獲取到就構造。
如果我們使用了EventIndex,也就是EventBus開啓了索引模式,那麼ignoreGeneratedIndex就爲false,如果沒有開啓索引模式ignoreGeneratedIndex則爲true。
最後把獲取的訂閱者信息加入到緩存裏。

我們看看在開啓了索引的情況下findUsingInfo(subscriberClass);這個方法做了什麼

EventBus
1、 findState.subscriberInfo = getSubscriberInfo(findState);獲取訂閱者信息
如果獲取到了訂閱者信息subscriberInfo,就可以通過findState.subscriberInfo.getSubscriberMethods();來獲取訂閱者的方法信息,然後判斷是否已經添加過該訂閱者的方法信息,如果沒有添加過則加入到訂閱者方法信息的一個List中
2、如果沒有獲取到訂閱者信息subscriberInfo則調用findUsingReflectionInSingleClass(findState);通過名字可以看出這是一個使用了放射來獲取訂閱者方法信息的方法。
3、最後通過getMethodsAndRelease(findState);方法來對FindState做處理,並返回所有訂閱者方法的List集合。
4、那麼什麼情況下可以獲取到訂閱者信息subscriberInfo呢?
我們要看一下 findState.subscriberInfo = getSubscriberInfo(findState);方法

EventBus如果findState.subscriberInfo不是空並且父類的subscriberInfo也不是空,那麼就獲取訂閱者信息並返回。
如果有一個是空,就判斷是否存在索引,如果存在則從索引裏獲取訂閱者信息並返回,如果不存在索引則返回null,然後外層函數會調用findUsingReflectionInSingleClass
EventBus
因爲在我們調用getSubscriberInfo方法之前執行了findState.initForSubscriber(subscriberClass);方法,在這個方法裏會把subscriberInfo置空,所以這個 **if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null)**正常情況下都是false,判斷貌似是多餘的,有知道這裏代碼的深意的大神請留言啊。
EventBus

如果沒有開啓索引則會調用findUsingReflection(subscriberClass);方法

EventBus
仍然是先構建了FindState,然後調用了 **findUsingReflectionInSingleClass(findState);**方法
注意這個方法就是在開啓了索引但是沒有找到訂閱者信息也會調用的方法,所以會有兩個地方調用此方法哦。
EvenBus

 private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                }
                ...
            }
        }
    }

methods = findState.clazz.getDeclaredMethods();通過反射來獲取訂閱者中所有的方法,並根據方法類型。參數和註解來找到訂閱方法,最後把所有的訂閱方法通過 findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));方法保存到findState對象中。

如何註冊訂閱者的

在查找完所有的訂閱者的訂閱方法後就要對所有的訂閱方法進行註冊。

 for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
  	}
    // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);//--------核心方法1---------//
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);//--------核心方法2---------//
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);//--------核心方法3---------//
                break;
            }
        }

        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);//--------核心方法4---------//
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

        if (subscriberMethod.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);
            }
        }
    }

核心方法:

Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

根據訂閱者和訂閱方法創建一個訂閱對象

CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);

根據eventType事件類型獲取訂閱者對象集合,如果集合爲Null則新建一個空集合,並將訂閱者集合根據事件類型保存在一個MAP中 subscriptionsByEventType.put(eventType, subscriptions);

subscriptions.add(i, newSubscription);

按照訂閱方法的優先級插入到訂閱對象集合中,完成訂閱方法的註冊。哈哈看到了吧,我們的訂閱方法的優先級屬性是在這裏進行應用的哦O(∩_∩)O~~

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

通過訂閱者subscriber獲取事件類型集合subscribedEvents,如果subscribedEvents爲nul則重新創建subscribedEvents,將新建的事件類型集合subscribedEvents存儲在MAP對象中,KEY值則是訂閱對象,最後將事件類型eventType加入到subscribedEvents中。

如果是黏性事件,則從黏性事件保存隊列stickyEvents中取出該事件類型的事件發送給當前訂閱者。這裏就是處理黏性事件的關鍵地方!
因爲黏性事件是在訂閱者註冊以後纔會發送的哦。正是因爲這樣,我們在會在註冊以後仍然可以接受到事件。
黏性事件
綜上:註冊方法主要做了兩件事
1、將訂閱對象集合subscriptions根據事件類型eventType(KEY值)添加到subscriptionsByEventType(MAP)中
將事件類型集合subscribedEvents根據訂閱者subscriber(KEY值)添加到typesBySubscriber(MAP)中
2、註冊以後發送黏性事件

這樣就完成了訂閱者的註冊過程

如何發送事件的

EventBus
EventBus
PostingThreadState postingState = currentPostingThreadState.get();
從PostingThreadState對象中取出事件隊列,然後將當前的事件插入到事件隊列eventQueue中,最後將隊列中的事件交給postSingleEvent方法處理,並移除該事件。
EventBus
eventInheritance表示是否繼續向上查找事件的父類,默認是true,可以通過EventBusBuilder設置。
當eventInheritance爲true的時候,則通過lookupAllEventTypes(eventClass);找到所有的父類事件並存在List中,最後通過postSingleEventForEventType方法發送事件。
EventBus

subscriptions = subscriptionsByEventType.get(eventClass);

根據事件類(KEY)從註冊時候的MAP中取出對應的訂閱對象集合

postToSubscription(subscription, event, postingState.isMainThread);

遍歷訂閱對象集合,將事件event和對應的訂閱對象傳遞給postToSubscription方法
EventBus
取出訂閱事件的線程模型threadMode,然後根據不同的線程模型執行不同的方法。
1、如果是threadMode主線程並且發送事件的線程也是主線程則直接通過反射運行訂閱的方法
2、如果threadMode不是主線程則使用mainThreadPoster將訂閱事件添加到主線程隊列中,mainThreadPoster其實是一個handler,通過handler將訂閱方法切換到主線程執行。另外backgroundPoster和asyncPoster其實是一個Runnable
3、如果threadMode是POSTING,則不切換線程,發送事件的方法在什麼線程就發送到什麼線程,在使用這個threadMode的時候應當避免在主線程發送,以免造成主線程阻塞。

如何取消註冊的

EventBus
EventBus

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

從MAP中根據訂閱者KEY找到事件類型集合

unsubscribeByEventType(subscriber, eventType);

遍歷事件類型集合並調用unsubscribeByEventType

typesBySubscriber.remove(subscriber);

把訂閱者對應的事件類型從事件類型集合的MAP中移除
我們再看看unsubscribeByEventType(Object subscriber, Class<?> eventType) 方法
EventBus

subscriptionsByEventType.get(eventType);

從MAP中根據事件類型KEY找到訂閱者對象集合

subscriptions.remove(i);

遍歷訂閱者對象集合,判斷註冊的時候傳進來的訂閱對象和現在要註銷的訂閱對象unregister(Object subscriber)是同一個對象就從訂閱者對象集合中移除該訂閱者。

在這裏介紹一個小知識:MAP裏保存了list,當你獲取到該list對象的引用的時候,對其進行增刪操作會同步到MAP裏,而不用再手動put一次。
實驗代碼:

               List<String> strs = new ArrayList<>();
               for (int i =0;i<5;i++){
                   strs.add("i = "+i);
               }
               
                Map<String, List<String>> map = new HashMap<>();
                map.put("str",strs);//保存list
                
                List<String> strs2 = map.get("str");//取出保存的list
                for (String str :
                        strs2) {
                    Log.i("LHD", "處理前的map裏的strs = " + str);
                }
                strs2.remove(0);//移除一個0,還剩1234
                strs2.remove(1);//移除一個2,還剩134

                List<String> strs3 = map.get("str");//獲取MAP裏的list
                for (String str :
                        strs3) {
                    Log.i("LHD", "處理後的map裏的strs = " + str);
                }

打印的值:
MAP裏保存List
以上就是EventBus3.0的源碼解析啦O(∩_∩)O~~

補充:

一句話總結EventBus的訂閱流程:在register註冊的時候,獲取到當前activity下的所有訂閱方法,並根據訂閱方法的要處理的事件類,將訂閱方法加入到對應的list中。

首先在註冊的時候,EventBus會獲取到當前activity的所有的訂閱方法,然後把這些訂閱方法根據事件類型加入到一個MAP中,這個MAP的KEY是訂閱類型,VALUE是一個list,而這個list就是所有接受對應Event事件的方法集合。

比如我們在activity中註冊
Eventbus註冊流程
EventBus會把當前activity下所有的訂閱方法都註冊到一個MAP裏。
KEY 是 eventType 也就是TestEvent.class
VALUE 是CopyOnWriteArrayList,也就是所有要消費TestEvent事件的訂閱方法的集合。
Subscription記錄了訂閱方法所在的activity和對應的SubscriberMethod,而SubscriberMethod則是將訂閱方法method和eventType,線程模型,優先級,時候是粘性等屬性封裝成的一個包裝類。
它們的關係如下:

new Subscription(subscriber, subscriberMethod)
new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky())

也就是將getTestEvent(TestEvent testEvent)這個方法和他的註解參數一起封裝成了一個SubscriberMethod對象。然後再和MainAcitivity.class一起封裝成了一個Subscription對象。
這樣當EventBus發送事件的時候就會根據eventType也就是TestEvent.class爲key,拿出所有TestEvent對應的訂閱方法SubscriberMethod,然後根據優先級線程模型和是否是粘性等條件通知所有TestEvent的訂閱方法。

//訂閱方法僞代碼:

    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//1、獲取當前訂閱方法的eventType,其實就是訂閱方法的參數的類
        Class<?> eventType = subscriberMethod.eventType;//TestEvent.class
//2、構建一個新的Subscription 對象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);//MainActivity.class,getTestEvent
 	//3、根據TestEvent拿出對應的list
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        int size = subscriptions.size();
//4、根據訂閱方法的優先級屬性進行排序,把訂閱方法加入到list中去
        for (int i = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);//然後排序list,並把新的Subscription放進去
                break;
            }
        }

//保存所有的事件類型
        subscribedEvents.add(eventType);
//發送粘性事件
       Object stickyEvent = stickyEvents.get(eventType);
      checkPostStickyEventToSubscription(newSubscription, stickyEvent);
    }
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章