Android 源碼分析之 EventBus 的源碼解析

一、EventBus 的使用

1.1 EventBus 簡介

EventBus 是一款用於 Android 的事件發佈-訂閱總線,由 GreenRobot 開發,Gihub 地址是:EventBus。它簡化了應用程序內各個組件之間進行通訊的複雜度,尤爲是碎片之間進行通訊的問題,能夠避免因爲使用廣播通訊而帶來的諸多不便。java

首先是 EventBus 的三個重要角色git

  1. Event:事件,它能夠是任意類型,EventBus 會根據事件類型進行全局的通知。
  2. Subscriber:事件訂閱者,在 EventBus 3.0 以前咱們必須定義以onEvent開頭的那幾個方法,分別是 onEvent()onEventMainThread()onEventBackgroundThread()onEventAsync(),而在3.0以後事件處理的方法名能夠隨意取,不過須要加上註解@subscribe,而且指定線程模型,默認是POSTING
  3. Publisher:事件的發佈者,能夠在任意線程裏發佈事件。通常狀況下,使用 EventBus.getDefault() 就能夠獲得一個EventBus對象,而後再調用 post(Object) 方法發佈事件便可。

其次是 EventBus 的四種線程模型(EventBus3.0),分別是:github

  1. POSTING:默認,表示事件處理函數的線程跟發佈事件的線程在同一個線程。
  2. MAIN:表示事件處理函數的線程在主線程(UI)線程,所以在這裏不能進行耗時操做。
  3. BACKGROUND:表示事件處理函數的線程在後臺線程,所以不能進行UI操做。若是發佈事件的線程是主線程(UI線程),那麼事件處理函數將會開啓一個後臺線程,若是果發佈事件的線程是在後臺線程,那麼事件處理函數就使用該線程。
  4. ASYNC:表示不管事件發佈的線程是哪個,事件處理函數始終會新建一個子線程運行,一樣不能進行UI操做。

1.2 使用 EventBus

在使用以前先要引入以下依賴:數組

implementation 'org.greenrobot:eventbus:3.1.1'
複製代碼

而後,咱們定義一個事件的封裝對象。在程序內部就使用該對象做爲通訊的信息:緩存

public class MessageWrap {

    public final String message;

    public static MessageWrap getInstance(String message) {
        return new MessageWrap(message);
    }

    private MessageWrap(String message) {
        this.message = message;
    }
}
複製代碼

而後,咱們定義一個 Activity 要拿過來測試事件發佈的效果:安全

@Route(path = BaseConstants.LIBRARY_EVENT_BUS_ACTIVITY1)
public class EventBusActivity1 extends CommonActivity<ActivityEventBus1Binding> {

    @Override
    protected void doCreateView(Bundle savedInstanceState) {
        // 爲按鈕添加添加單擊事件
        getBinding().btnReg.setOnClickListener(v -> EventBus.getDefault().register(this));
        getBinding().btnNav2.setOnClickListener( v ->
                ARouter.getInstance()
                        .build(BaseConstants.LIBRARY_EVENT_BUS_ACTIVITY2)
                        .navigation());
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onGetMessage(MessageWrap message) {
        getBinding().tvMessage.setText(message.message);
    }
}
複製代碼

這裏咱們當按下按鈕的時候向 EventBus 註冊監聽,而後按下另外一個按鈕的時候跳轉到拎一個 Activity,並在另外一個 Activity 發佈咱們輸入的事件。在上面的 Activity 中,咱們會添加一個監聽的方法,即 onGetMessage(),這裏咱們須要爲其加入註解 @Subscribe 並指定線程模型爲主線程 MAIN。最後,就是在 Activity 的 onDestroy() 方法中取消註冊該 Activity。數據結構

下面是另外一個 Activity 的定義,在這個 Activity 中,咱們當按下按鈕的時候從 EditText 中取出內容並進行發佈,而後咱們退出到以前的 Activity,以測試是否正確監聽到發佈的內容:async

@Route(path = BaseConstants.LIBRARY_EVENT_BUS_ACTIVITY2)
public class EventBusActivity2 extends CommonActivity<ActivityEventBus2Binding> {

    @Override
    protected void doCreateView(Bundle savedInstanceState) {
        getBinding().btnPublish.setOnClickListener(v -> publishContent());
    }

    private void publishContent() {
        String msg = getBinding().etMessage.getText().toString();
        EventBus.getDefault().post(MessageWrap.getInstance(msg));
        ToastUtils.makeToast("Published : " + msg);
    }
}
複製代碼

根據測試的結果,咱們的確成功地接收到了發送的信息。ide

1.3 黏性事件

所謂的黏性事件,就是指發送了該事件以後再訂閱者依然可以接收到的事件。使用黏性事件的時候有兩個地方須要作些修改。一個是訂閱事件的地方,這裏咱們在先打開的 Activity 中註冊監聽黏性事件:函數

@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onGetStickyEvent(MessageWrap message) {
    String txt = "Sticky event: " + message.message;
    getBinding().tvStickyMessage.setText(txt);
}
複製代碼

另外一個是發佈事件的地方,這裏咱們在新的開的 Activity 中發佈黏性事件。即調用 EventBus 的 postSticky() 方法來發布事件:

private void publishStickyontent() {
    String msg = getBinding().etMessage.getText().toString();
    EventBus.getDefault().postSticky(MessageWrap.getInstance(msg));
    ToastUtils.makeToast("Published : " + msg);
}
複製代碼

按照上面的模式,咱們先在第一個 Activity 中打開第二個 Activity,而後在第二個 Activity 中發佈黏性事件,並回到第一個 Activity 註冊 EventBus。根據測試結果,當按下注冊按鈕的時候,會當即觸發上面的訂閱方法從而獲取到了黏性事件。

1.4 優先級

@Subscribe 註解中總共有3個參數,上面咱們用到了其中的兩個,這裏咱們使用如下第三個參數,即 priority。它用來指定訂閱方法的優先級,是一個整數類型的值,默認是 0,值越大表示優先級越大。在某個事件被髮布出來的時候,優先級較高的訂閱方法會首先接受到事件。

爲了對優先級進行測試,這裏咱們須要對上面的代碼進行一些修改。這裏,咱們使用一個布爾類型的變量來判斷是否應該取消事件的分發。咱們在一個較高優先級的方法中經過該布爾值進行判斷,若是未 true 就中止該事件的繼續分發,從而經過低優先級的訂閱方法沒法獲取到事件來證實優先級較高的訂閱方法率先獲取到了事件。

這裏有幾個地方須要注意

  1. 只有當兩個訂閱方法使用相同的ThreadMode參數的時候,它們的優先級纔會與priority指定的值一致;
  2. 只有當某個訂閱方法的ThreadMode參數爲POSTING的時候,它才能中止該事件的繼續分發。

因此,根據以上的內容,咱們須要對代碼作以下的調整:

// 用來判斷是否須要中止事件的繼續分發
private boolean stopDelivery = false;

@Override
protected void doCreateView(Bundle savedInstanceState) {
    // ...

    getBinding().btnStop.setOnClickListener(v -> stopDelivery = true);
}

@Subscribe(threadMode = ThreadMode.POSTING, priority = 0)
public void onGetMessage(MessageWrap message) {
    getBinding().tvMessage.setText(message.message);
}

// 訂閱方法,須要與上面的方法的threadMode一致,而且優先級略高
@Subscribe(threadMode = ThreadMode.POSTING, sticky = true, priority = 1)
public void onGetStickyEvent(MessageWrap message) {
    String txt = "Sticky event: " + message.message;
    getBinding().tvStickyMessage.setText(txt);
    if (stopDelivery) {
        // 終止事件的繼續分發
        EventBus.getDefault().cancelEventDelivery(message);
    }
}
複製代碼

即咱們在以前的代碼之上增長了一個按鈕,用來將 stopDelivery 的值置爲 true。該字段隨後將會被用來判斷是否要終止事件的繼續分發,由於咱們須要在代碼中中止事件的繼續分發,因此,咱們須要將上面的兩個訂閱方法的 threadMode 的值都置爲ThreadMode.POSTING

按照,上面的測試方式,首先咱們在當前的 Activity 註冊監聽,而後跳轉到另外一個 Activity,發佈事件並返回。第一次的時候,這裏的兩個訂閱方法都會被觸發。而後,咱們按下中止分發的按鈕,並再次執行上面的邏輯,此時只有優先級較高的方法獲取到了事件並將該事件終止。

上面的內容是 EventBus 的基本使用方法,相關的源碼參考:Github

二、源碼分析

在分析 EventBus 源碼的時候,咱們先從獲取一個 EventBus 實例的方法入手,而後再分別看一下它的註冊、取消註冊、發佈事件以及觸發觀察方法的代碼是如何實現的。在下面的文章中咱們將會回答如下幾個問題:

  1. 在 EventBus 中,使用 @Subscribe 註解的時候指定的 ThreadMode 是如何實如今不一樣線程間傳遞數據的?
  2. 使用註解和反射的時候的效率問題,是否會像 Guava 的 EventBus 同樣有緩存優化?
  3. 黏性事件是不是經過內部維護了以前發佈的數據來實現的,是否使用了緩存?

2.1 獲取實例的過程

在建立 EventBus 實例的時候,一種方式是按照咱們上面的形式,經過 EventBus 的靜態方法 getDefault() 來獲取一個實例。getDefault() 自己會調用其內部的構造方法,經過傳入一個默認 的EventBusBuilder 來建立 EventBus。此外,咱們還能夠直接經過 EventBus 的 builder() 方法獲取一個 EventBusBuilder 的實例,而後經過該構建者模式來個性化地定製本身的 EventBus。即:

// 靜態的單例實例
static volatile EventBus defaultInstance;

// 默認的構建者
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

// 實際上使用了DCL雙檢鎖機制,這裏簡化了一下
public static EventBus getDefault() {
    if (defaultInstance == null) defaultInstance = new EventBus();
    return defaultInstance;
}

public EventBus() {
    this(DEFAULT_BUILDER);
}

// 調用getDefault的時候,最終會調用該方法,使用DEFAULT_BUILDER建立一個實例
EventBus(EventBusBuilder builder) {
    // ...
}

// 也可使用下面的方法獲取一個構建者,而後使用它來個性化定製EventBus
public static EventBusBuilder builder() {
    return new EventBusBuilder();
}
複製代碼

2.2 註冊

當調用 EventBus 實例的 register() 方法的時候,會執行下面的邏輯:

public void register(Object subscriber) {
    // 首席會獲取註冊的對象的類型
    Class<?> subscriberClass = subscriber.getClass();
    // 而後獲取註冊的對象的訂閱方法
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    // 對當前實例加鎖,並不斷執行監聽的邏輯
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            // 對訂閱方法進行註冊
            subscribe(subscriber, subscriberMethod);
        }
    }
}
複製代碼

這裏的 SubscriberMethod 封裝了訂閱方法(使用 @Subscribe 註解的方法)類型的信息,它的定義以下所示。從下面能夠的代碼中咱們能夠看出,實際上該類就是經過幾個字段來存儲 @Subscribe 註解中指定的類型信息,以及一個方法的類型變量。

public class SubscriberMethod {
    final Method method;
    final ThreadMode threadMode;
    final Class<?> eventType;
    final int priority;
    final boolean sticky;

    // ...
}
複製代碼

register() 方法經過 subscriberMethodFinder 實例的 findSubscriberMethods() 方法來獲取該觀察者類型中的全部訂閱方法,而後將全部的訂閱方法分別進行訂閱。下面咱們先看下查找訂閱者的方法。

查找訂閱者的訂閱方法

下面是 SubscriberMethodFinder 中的 findSubscriberMethods() 方法:

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(...);
    } else {
        // 將獲取到的訂閱方法放置到緩存當中
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}
複製代碼

這裏咱們先從緩存當中嘗試獲取某個觀察者中的全部訂閱方法,若是沒有可用緩存的話就從該類中查找訂閱方法,並在返回結果以前將這些方法信息放置到緩存當中。這裏的 ignoreGeneratedIndex 參數表示是否忽略註解器生成的 MyEventBusIndex,該值默認爲 false。而後,咱們會進入到下面的方法中獲取訂閱方法信息:

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    // 這裏經過FindState對象來存儲找到的方法信息
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    // 這裏是一個循環操做,會從當前類開始遍歷該類的全部父類
    while (findState.clazz != null) {
        // 獲取訂閱者信息
        findState.subscriberInfo = getSubscriberInfo(findState); // 1
        if (findState.subscriberInfo != null) {
            // 若是使用了MyEventBusIndex,將會進入到這裏並獲取訂閱方法信息
            SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
            for (SubscriberMethod subscriberMethod : array) {
                if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                    findState.subscriberMethods.add(subscriberMethod);
                }
            }
        } else {
            // 未使用MyEventBusIndex將會進入這裏使用反射獲取方法信息
            findUsingReflectionInSingleClass(findState); // 2
        }
        // 將findState.clazz設置爲當前的findState.clazz的父類
        findState.moveToSuperclass();
    }
    return getMethodsAndRelease(findState);
}
複製代碼

在上面的代碼中,會從當前訂閱者類開始直到它最頂層的父類進行遍從來獲取訂閱方法信息。這裏在循環的內部會根據咱們是否使用了 MyEventBusIndex 走兩條路線,對於咱們沒有使用它的,會直接使用反射來獲取訂閱方法信息,即進入2處。

下面是使用反射從訂閱者中獲得訂閱方法的代碼:

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // 獲取該類中聲明的全部方法
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        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()));
                    }
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(...);
            }
        } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
            String methodName = method.getDeclaringClass().getName() + "." + method.getName();
            throw new EventBusException(...);
        }
    }
}
複製代碼

這裏會對當前類中聲明的全部方法進行校驗,並將符合要求的方法的信息封裝成一個SubscriberMethod對象塞到列表中。

註冊訂閱方法

直到了如何拿到全部的訂閱方法以後,咱們回到以前的代碼,看下訂閱過程當中的邏輯:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    // 將全部的觀察者和訂閱方法封裝成一個Subscription對象
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod); // 1
    // 嘗試從緩存中根據事件類型來獲取全部的Subscription對象
    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(...);
        }
    }

    // 這裏會根據新加入的方法的優先級決定插入到隊列中的位置
    int size = subscriptions.size(); // 2
    for (int i = 0; i <= size; i++) {
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    // 這裏又會從「訂閱者-事件類型」列表中嘗試獲取該訂閱者對應的全部事件類型
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); // 3
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

    // 若是是黏性事件還要進行以下的處理
    if (subscriberMethod.sticky) { // 4
        if (eventInheritance) {
            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);
        }
    }
}
複製代碼

這裏涉及到了幾個集合,它們是用來作緩存的,還有就是來維護觀察者、事件類型和訂閱方法之間的關係的。註冊觀察的方法比較長,咱們能夠一點一點來看。首先,會在代碼1處將觀察者和訂閱方法封裝成一個 Subscription 對象。而後,在2處用到了 CopyOnWriteArrayList 這個集合,它是一種適用於多讀寫少場景的數據結構,是一種線程安全的數組型的數據結構,主要用來存儲一個事件類型所對應的所有的 Subscription 對象。EventBus在這裏經過一個 Map<Class<?>, CopyOnWriteArrayList<Subscription>> 類型的哈希表來維護這個映射關係。而後,咱們的程序執行到2處,在這裏會對 Subscription 對象的列表進行遍歷,並根據訂閱方法的優先級,爲當前的 Subscription 對象尋找一個合適的位置。3的地方主要的邏輯是獲取指定的觀察者對應的所有的觀察事件類型,這裏也是經過一個哈希表來維護這種映射關係的。而後,在代碼 4 處,程序會根據當前的訂閱方法是不是黏性的,來決定是否將當前緩存中的信息發送給新訂閱的方法。這裏會經過 checkPostStickyEventToSubscription() 方法來發送信息,它內部的實現的邏輯和 post() 方法相似,咱們再也不進行說明。

取消註冊的邏輯比較比較簡單,基本上就是註冊操做反過來——將當前訂閱方法的信息從緩存中踢出來,咱們再也不進行分分析。下面咱們分析另外一個比較重要的地方,即發送事件相關的邏輯。

2.3 通知

通知的邏輯相對來講會比較複雜一些,由於這裏面涉及一些線程之間的操做。咱們看下下面的代碼吧:

public void post(Object event) {
    // 這裏從線程局部變量中取出當前線程的狀態信息
    PostingThreadState postingState = currentPostingThreadState.get();
    // 這裏是以上線程局部變量內部維護的一個事件隊列
    List<Object> eventQueue = postingState.eventQueue;
    // 將當前要發送的事件加入到隊列中
    eventQueue.add(event);

    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); // 1
            }
        } finally {
            // 恢復當前線程的信息
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}
複製代碼

這裏的 currentPostingThreadState 是一個 ThreadLocal 類型的變量,其中存儲了對應於當前線程的 PostingThreadState 對象,該對象中存儲了當前線程對應的事件列表和線程的狀態信息等。從上面的代碼中能夠看出,post() 方法會在1處不斷從當前線程對應的隊列中取出事件並進行發佈。下面咱們看如下這裏的 postSingleEvent() 方法。

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    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) {
            logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent 
                && eventClass != NoSubscriberEvent.class 
                && eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}
複製代碼

在上面的代碼中,咱們會根據 eventInheritance 的值決定是否要同時遍歷當前事件的全部父類的事件信息並進行分發。若是設置爲 true 就將執行這一操做,並最終使用 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;
}
複製代碼

在上面的代碼中,咱們會經過傳入的事件類型到緩存中取尋找它對應的所有的 Subscription,而後對獲得的 Subscription 列表進行遍歷,並依次調用 postToSubscription() 方法執行事件的發佈操做。下面是 postToSubscription() 方法的代碼,這裏咱們會根據訂閱方法指定的 threadMode信息來執行不一樣的發佈策略。

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 MAIN_ORDERED:
            if (mainThreadPoster != null) {
                mainThreadPoster.enqueue(subscription, event);
            } else {
                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(...);
    }
}
複製代碼

在上面的方法中,會根據當前的線程狀態和訂閱方法指定 的 threadMode 信息來決定合適觸發方法。這裏的 invokeSubscriber() 會在當前線程中當即調用反射來觸發指定的觀察者的訂閱方法。不然會根據具體的狀況將事件加入到不一樣的隊列中進行處理。這裏的mainThreadPoster 最終繼承自 Handler,當調用它的 enqueue() 方法的時候,它會發送一個事件並在它自身的 handleMessage() 方法中從隊列中取值並進行處理,從而達到在主線程中分發事件的目的。這裏的 backgroundPoster 實現了 Runnable 接口,它會在調用 enqueue() 方法的時候,拿到 EventBus 的 ExecutorService 實例,並使用它來執行本身。在它的 run() 方法中會從隊列中不斷取值來進行執行。

總結

以上就是Android中的EventBus的源碼分析,這裏咱們回答以前提出的幾個問題來做結:

  1. 在EventBus中,使用 @Subscribe 註解的時候指定的 ThreadMode 是如何實如今不一樣線程間傳遞數據的?

要求主線程中的事件經過 Handler 來實如今主線程中執行,非主線程的方法會使用 EventBus 內部的 ExecutorService 來執行。實際在觸發方法的時候會根據當前線程的狀態和訂閱方法的 ThreadMode 指定的線程狀態來決定什麼時候觸發方法。非主線程的邏輯會在 post() 的時候加入到一個隊列中被隨後執行。

  1. 使用註解和反射的時候的效率問題,是否會像 Guava 的 EventBus 同樣有緩存優化?

內部使用了緩存,確切來講就是維護了一些映射的關係。可是它的緩存沒有像 Guava 同樣使用軟引用之類方式進行優化,即一直是強引用類型的。

  1. 黏性事件是不是經過內部維護了以前發佈的數據來實現的,是否使用了緩存?

黏性事件會經過 EventBus 內部維護的一個事件類型-黏性事件的哈希表存儲,當註冊一個觀察者的時候,若是發現了它內部有黏性事件監聽,會執行 post() 相似的邏輯將事件當即發送給該觀察者。


若是您喜歡個人文章,能夠在如下平臺關注我:

相關文章
相關標籤/搜索