EventBus源碼分析

EventBus源碼分析

Android開發中咱們最經常使用到的能夠說就是EventBus了,今天咱們來深刻研究一下EventBus的源碼。java

使用簡介

先簡單回顧下EventBus最基本的使用方法:git

  • 首先建立一個數據類
public class MessageEvent {
 
    public final String message;
 
    public MessageEvent(String message) {
        this.message = message;
    }
}
  • 在相關的代碼中添加處理事件的方法:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
    Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
  • 在相應的生命週期中註冊/解綁
@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}
 
@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}
  • 最後一步,在你須要的地方發送相應的事件
EventBus.getDefault().post(new MessageEvent("Hello world!"));

源碼分析

使用EventBus基本須要四步,咱們分析源碼主要着重與後兩步,也就是三個方法: EventBus.getDefault().register(this);EventBus.getDefault().post(new MessageEvent("Hello world!"));EventBus.getDefault().unregister(this);。以這三個方法爲入口來切入EventBus的源碼分析,會使咱們對EventBus的整個流程更加清晰。github

register方法

public void register(Object subscriber) {
    
         //1.獲取訂閱者的Class對象
        Class<?> subscriberClass = subscriber.getClass();
        
        //經過反射獲取訂閱者中開發人員定義的處理事件的方法集合
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        
        //將獲取的訂閱者和訂閱者中處理事件的方法按照必定的規則相結合並存儲到EventBus內部
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

我把register這個方法主要分爲三部分。第一步很簡單了,獲取訂閱者的Class對象,這個訂閱者一般就是咱們寫的Activity或者Fragment等等。第二步是經過咱們第一步拿到的訂閱者的Class對象,反射獲取全部符合規則的處理事件的方法。既然要 符合規則的方法 ,那麼規則是什麼呢?其實規則就是這些方法必須加 @subscribe 註解,參數必須有且只有一個等等。咱們來看一下代碼:緩存

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    
        //經過緩存獲取處理事件方法集合,若是有緩存從緩存中取,若是沒有則進行下一步
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }

        //獲取處理事件方法集合
        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        
        //若是當前訂閱者中的處理事件方法集合爲空,則拋出異常;反之則加入緩存中並返回
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

findSubscriberMethods這個方法中,主要是進行了對訂閱者中咱們定義的處理事件的方法的獲取操做。代碼邏輯其實已經比較清晰了,首先是從緩存中拿方法集合,有的話直接返回,沒有的話再去獲取。第二步是獲取的關鍵,它比較複雜,咱們暫時先看第三步。若是當前獲取到的方法集合爲空,則拋出異常。看到這裏小夥伴們應該就比較熟悉了,在咱們剛開始使用EventBus的時候常常會在一個Activity中調用的EventBus的register方法,可是卻忘了寫@subscribe註解的方法,運行程序會報錯。那麼很明顯,就是這裏拋出了一個異常,這個異常提醒咱們,若是在訂閱者中調用的register方法,就必須添加至少一個相應的處理事件的方法。若是獲取到的方法集合不爲空,則將之放入緩存並返回。併發

這個findSubscriberMethods方法中最關鍵的是第二步的獲取。這裏有一個變量 ignoreGeneratedIndex,是否忽略索引,默認值爲false,也就是默認不忽略索引,走的是else中的 findUsingInfo(subscriberClass); 方法。那麼問題來了,這個索引是什麼,爲何咱們日常使用EventBus的時候歷來都沒用過這個東西?其實在EventBus3.0以前是沒有索引這個東西的,第二步獲取方法集合走的是 findUsingReflection(subscriberClass); 這個方法。那爲何在3.0以後加上了索引呢?其實答案很簡單,就是由於反射獲取方法集合是比較低效的,使用反射所耗費的時間開銷比較大。若是使用索引,EventBus的性能會提高一倍。具體如何在你的代碼中使用EventBus的索引,能夠參考 greenrobot官網中index的介紹 。那麼小夥伴們問題又來了,既然是默認不忽略索引,而咱們平時使用EventBus根本沒添加過索引相關的代碼,那 findUsingInfo(subscriberClass); 這段代碼到底是使用什麼方式來獲取方法集合的呢?放代碼:async

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);
            
            //若是咱們沒有添加相關索引的配置,那麼findState.subscriberInfo=null,走的是else中的方法
            if (findState.subscriberInfo != null) {
                //添加了索引配置,走這裏
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                //沒有添加索引配置,走這裏,經過方法名也能看出來,若是沒添加索引配置,使用的仍是反射。
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

findUsingInfo 這段代碼中,咱們忽略細節,只看我添加註釋的部分。若是添加了索引相關的配置,代碼會去執行索引相關的邏輯;反之,代碼執行 findUsingReflectionInSingleClass(findState); 這個函數,經過反射來獲取方法集合。咱們忽略索引部分,跟進 findUsingReflectionInSingleClass(findState); 這個方法看一下:ide

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();
            
            //必須是PUBLIC方法.....
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                
                //參數個數只能爲1
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    
                    //必須有@subscribe註解
                    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()));
                        }
                    }
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                    throw new EventBusException("@Subscribe method " + methodName +
                            "must have exactly 1 parameter but has " + parameterTypes.length);
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }
        }
    }

這個方法就很清楚了。首先經過反射拿到全部方法的集合,而後遍歷這個集合,篩選出符合規則的方法,也就是咱們寫的處理事件的方法。到這裏小夥伴們應該很清楚了,變量ignoreGeneratedIndex默認是false,走的是索引的處理,可是我平時沒寫過索引的配置,因此最後仍是走的反射,經過反射獲取方法集合。函數

register方法中的第二步到這裏就大體說完了,下面咱們開始第三步。代碼上面有,我再貼一遍方便你們看。oop

synchronized (this) {
            //遍歷方法集合
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }

這段代碼很簡單,遍歷第二步獲取的方法集合,調用 subscribe(subscriber, subscriberMethod); 這個方法。很明顯,subscribe(subscriber, subscriberMethod); 是最後一步的核心代碼,咱們跟進去看一下:源碼分析

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        
        //獲取方法中的事件類型
        Class<?> eventType = subscriberMethod.eventType;
        
        //將訂閱者和方法綁在一塊兒造成一個Subscription對象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        
        //將newSubscription加入eventType所對應的subscriptions集合中
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        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();
        //按照priority優先級將newSubscription加入subscriptions集合中
        for (int i = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

        //初始化 typesBySubscriber 這個成員變量,造成 訂閱者 -> 訂閱者可以接收的事件類型集合 的關係
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        //加入新的事件類型
        subscribedEvents.add(eventType);

        //若是方法能夠接收sticky事件
        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>).
                
                //遍歷stickEvents這個map
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    
                    //若是找到了跟這個方法的eventType一致的stickEvent
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
                        
                        //發送事件,本質是經過反射直接調用這個方法
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                //
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

這段代碼就比較多了,咱們來捋一下它的思路。這個方法接收2個參數Object subscriber, SubscriberMethod subscriberMethod,第一個參數是咱們的訂閱者,第二個參數是對咱們寫的處理事件的方法的封裝。方法內部首先經過Class<?> eventType = subscriberMethod.eventType;獲取了一個eventType,表明的是處理事件方法中參數的類型,比方說:

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
    Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}

在這個例子中eventType指的就是MessageEvent.Class。下一步經過Subscription newSubscription = new Subscription(subscriber, subscriberMethod);將訂閱者和方法封裝綁定。接下來的話subscriptionsByEventType這個變量是EventBus中的一個成員變量,經過名字咱們也能夠知道,這個一個map,它的主要功能就是經過eventType來獲取subscriptions這個Subscription類的集合。咱們看一下這個成員變量的定義:

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

跟咱們說的同樣,subscriptionsByEventType是一個map,它的key存的是eventType,value存的是Subscription的集合。繼續看下一段代碼:

//按照priority優先級將newSubscription加入subscriptions集合中
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);
                break;
            }
        }

記不記得若是要確保事件的處理須要有前後順序,那麼咱們須要在事件處理方法上添加priority的註解。這段代碼就是把優先級高的放到list的首部,優先級低的放到後面,經過這種方式實現了不一樣方法對同一事件處理的優先級問題。繼續看代碼:

//初始化 typesBySubscriber 這個成員變量,造成 訂閱者 -> 訂閱者可以接收的事件類型集合 的關係
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

這段代碼核心是初始化typesBySubscriber這個成員變量,它的結構和subscriptionsByEventType相似,key是subscriber訂閱者,value是eventType的集合。接下來是最後一段代碼:

//若是方法能夠接收sticky事件
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>).
                
                //遍歷stickEvents這個map
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    
                    //若是找到了跟這個方法的eventType一致或是其子類的stickEvent
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
                        
                        //發送事件,本質是經過反射直接調用這個方法
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }

粘性事件相信你們都用過,這段代碼首先判斷了處理事件的方法是否有stick這個註解,若是存在則進入下一步。eventInheritance這個變量默認爲true,表明事件是否具備繼承性。能夠這麼理解,發送一個事件或者發送這個事件類型的子類,EventBus的處理事件方法中的參數類型是父類型,那麼這個方法既能夠收到父類型的事件,也能夠收到子類型的事件。那小夥伴們要問了,那若是處理事件方法中的參數類型是子類型呢?那這個方法就只能接收子類型的事件,而沒法接收父類型的事件。在 if (eventType.isAssignableFrom(candidateEventType))這一行代碼作了判斷,isAssignableFrom這個方法是JDK中Class類中的一個方法,做用就是判斷前者是不是與後者類型一致或前者是後者的父類。下面這個代碼就很簡單了checkPostStickyEventToSubscription(newSubscription, stickyEvent);發送事件,咱們跟進去看一下:

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
            // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
            // --> Strange corner case, which we don't take care of here.
            postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
        }
    }

這個方法的功能很簡單,對事件作了非空校驗,而後調用了postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());方法,這個方法本質就是經過反射調用method.invoke(subscription.subscriber, event);來執行咱們定義的對事件的處理方法。這個方法很重要,我會在講post方法的時候着重分析,咱們暫時只須要知道它的功能便可。看到這裏,小夥伴們應該就能明白,爲何在粘性事件發出後再註冊的事件處理方法能夠接收到它了,由於以前發送的stickyEvent都會存入stickyEvents這個map類型的EventBus中的成員變量中,當咱們註冊調用register方法時,在register方法內部會直接經過checkPostStickyEventToSubscription(newSubscription, stickyEvent);來執行咱們定義的粘性事件處理方法。

至此,咱們終於說完了register這個方法,這個方法的內容看似只有短短几行,但它所作的事情仍是很繁瑣複雜的。下面咱們來對上面對register的分析來作個總結:

  • 經過反射獲取訂閱者中定義的處理事件的方法集合
  • 遍歷方法集合,調用subscribe方法,在這個subscribe方法內:

    • 初始化並完善subscriptionsByEventType這個map對象
    • 初始化並完善typesBySubscriber這個map對象
    • 觸發stick事件的處理

post方法

咱們經常使用的post有兩種:post和postSticky,先來看postSticky:

public void postSticky(Object event) {
        synchronized (stickyEvents) {
            
            //全部發出的sticky事件都會存入stickyEvents這個map中
            stickyEvents.put(event.getClass(), event);
        }
        // 調用普通的post方法
        post(event);
    }

postSticky結構分爲兩塊:將全部發出的sticky事件存入stickyEvents這個map中,關於stickyEvents這個EventBus中的成員變量咱們在register方法中已經說過,再也不贅述。以後會調用post方法,因此咱們的重點仍是post方法:

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循環來不斷髮送事件隊列中的事件,直到事件隊列爲空
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

忽略代碼細節的話,post仍是很好理解的。在發送事件的當前線程中會存儲一個線程狀態的變量postingState,這個變量包含了當前線程中的一些重要信息,咱們來看一下它的組成:

final static class PostingThreadState {
        //事件隊列
        final List<Object> eventQueue = new ArrayList<Object>();
        //是否正在發送事件
        boolean isPosting;
        //是不是主線程
        boolean isMainThread;
        Subscription subscription;
        Object event;
        //是否已經取消
        boolean canceled;
    }

能夠看到,PostingThreadState爲當前線程存儲了不少有用的信息:事件隊列、是否正在發送事件、是不是主線程、subscription、事件、是否已經取消。

繼續看post的代碼,將發送的事件加入到了當前線程中的事件隊列中。以後就是一個while循環,不斷髮送事件隊列中的事件,直到事件隊列中再也不包含任何事件。循環中發送事件的方法是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();
            
            //循環發送事件
            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) {
                Log.d(TAG, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

這個方法核心只有兩部分:獲取事件類型、發送事件。有一個變量你們可能會有些疑惑,eventInheritance表明是否容許事件發送這個事件的全部父類型事件,默認爲true,也就是說在發送一個子類事件時,EventType是父類的事件處理方法也能夠收到當前事件。舉個栗子:

@Subscribe()
    public fun onReceiveObject(data: Any) {
        Observable.timer(
                1,
                TimeUnit.SECONDS).observeOn(AndroidSchedulers.mainThread()).subscribe() {
            toast("any")
        }

    }

    @Subscribe()
    public fun onReceiveDataItem(data: DataItem) {
        Observable.timer(
                2,
                TimeUnit.SECONDS).observeOn(AndroidSchedulers.mainThread()).subscribe() {
            toast("dataItem")
        }
    }

這是兩個kotlin寫的事件處理方法,第一個接收的事件類型是Any,對kotlin不熟悉的小夥伴能夠理解爲Object類型;第二個接收的事件類型是DataItem。咱們都知道,Object類確定是DataItem的父類。我下面提兩個問題,若是發送Object類型的事件,會發生什麼?若是發送DataItem類型的事件,會發生什麼?

相信若是你們真正理解了postSingleEvent這個方法的話,這個問題不難回答。若是發送Object類型,會顯示any的Toast;若是發送DataItem類型,則會顯示anydataItem的Toast。從上面的代碼分析咱們能夠知道,在發送一個事件的時候,EventBus默認會找到這個事件類型全部的父類型併發送。

postSingleEvent這個方法中發送事件的方法是postSingleEventForEventType,咱們繼續跟進去看代碼:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        
        //根據事件類型找到subscriptions
        synchronized (this) {
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        
        //變量subscriptions,發送事件
        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;
    }

代碼本事邏輯很簡單:根據subscriptionsByEventType找到事件eventClass對應的subscriptions,相信這些變量你們都很熟悉了。以後會遍歷subscriptions,調用postToSubscription發送事件,咱們繼續跟進:

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 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表明着當前線程,在哪一個線程發送事件,就在哪一個線程處理事件;MAIN表明只在主線程處理事件;BACKGROUND表明只在非主線程處理事件;ASYNC也是表明在非主線程處理事件。

BACKGROUNDASYNC都是在非主線程處理事件,那麼兩者有什麼區別呢?
從代碼中能夠直觀的看到:BACKGROUND的邏輯是若是在主線程,那麼會開啓一條新的線程處理事件,若是不在主線程,那麼直接處理事件;而ASYNC的邏輯則是無論你處於什麼線程,我都新開一條線程處理事件。

咱們從上到下把這四種類型解釋一下:
POSTING中調用了invokeSubscriber方法,跟進去看一下:

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

核心代碼只有一句:subscription.subscriberMethod.method.invoke(subscription.subscriber, event);也很好理解,經過反射執行事件處理方法。

MAIN中首先判斷當前線程是不是主線程,若是是,則執行invokeSubscriber,經過反射直接執行事件處理方法。反之則經過mainThreadPoster.enqueue(subscription, event);來執行事件的處理,那麼它是如何切換線程的呢?答案很簡單mainThreadPoster實際上是一個主線程中的Handler,調用mainThreadPoster.enqueue(subscription, event);會觸發它的handleMessage方法,在這個方法中最終調用的是eventBus.invokeSubscriber(pendingPost);。能夠看到經過handler機制進行了線程的切換,在handleMessage中依舊是調用invokeSubscriber方法經過反射來執行事件處理方法。

BACKGROUND中首先判斷是否在主線程,若是是則調用backgroundPoster.enqueue(subscription, event);backgroundPostermainThreadPoster不同,backgroundPoster是一個Runnableenqueue其實是開啓了一個線程,在這個新的線程中再執行invokeSubscriber這個方法。若是不在主線程,則直接調用invokeSubscriber

ASYNCasyncPoster和上面提到的backgroundPoster同樣,也是一個Runnable,它的enqueue實際上也是開啓了一個線程,在這個新的線程中再執行invokeSubscriber這個方法。

至此爲止post方法終於講完了,咱們來總結一下:

  • 將事件加入當前線程的事件隊列
  • 遍歷這個事件隊列

    • 找到當前事件類型全部的父類事件類型,加入事件類型集合並遍歷

      • 經過subscriptionsByEventType獲取subscriptions集合,遍歷這個subscriptions集合

        • 在POSTING、MAIN、BACKGROUND、ASYNC四種線程模式下經過反射執行事件處理方法

unregister

終於到最後一個方法了,unregister方法。不廢話,看代碼:

public synchronized void unregister(Object subscriber) {
    
        //獲取訂閱者全部訂閱的事件類型
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            
            //遍歷事件類型集合
            for (Class<?> eventType : subscribedTypes) {
                
                //從subscriptionsByEventType中刪除訂閱者的相關信息
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

這個方法的核心內容就是刪除全局變量subscriptionsByEventType中全部包含當前訂閱者的subscription,這也就很好的解釋了爲何在解綁後咱們不會再收到EventBus發送的事件了。由於發送事件的核心是根據事件類型從subscriptionsByEventType中獲取subscriptions這個集合,遍歷集合經過subscription能夠拿到subscribersubscriberMethod,以後再經過subscription.subscriberMethod.method.invoke(subscription.subscriber, event);,採用反射來進行事件的處理。unregister方法刪除了全局變量subscriptionsByEventType中全部包含當前訂閱者的subscription,在遍歷subscriptions的時候是不會獲取包含當前訂閱者的subscription,因此天然不會再收到事件。

觀察者模式與EventBus

其實看完上面的分析,熟悉觀察者模式的小夥伴們應該很清楚了,搞了這麼多,其實EventBus不就是個觀察者模式麼。沒錯,EventBus的本質就是個觀察者模式,常見的被觀察者只有一個,而觀察者的個數不肯定。被觀察者內部維持着一個觀察者的集合,當被觀察者要發送事件時會遍歷內部的觀察者集合,拿到觀察者並調用觀察者的相關方法。觀察者的描述是否是和EventBus的整個流程很是一致?簡而言之,EventBus就是一個被觀察者,它的內部存放着一個subscriptionsByEventType集合,這個集合包含了咱們全部的觀察者,也就是調用了register的全部Activity或者Fragment等等。當使用post發送事件時,會遍歷subscriptionsByEventType,拿到觀察者,經過反射調用觀察者中的事件處理方法。你沒看錯,EventBus的核心邏輯就是這麼簡單,僅僅只是一個觀察者模式。

再多扯幾句,有些小夥伴可能看完上面一大堆東西仍是感受不是很明白,不要緊,很正常,誰也不太可能任何東西看一遍就瞭然於胸的。個人建議是能夠打開Android Studio邊看源碼邊看個人分析,不明白的跟進去看源碼就好。

最後,但願你們能經過這篇分析加深對EventBus和觀察者模式的理解,感謝你們。

相關文章
相關標籤/搜索