Android開源庫——EventBus原始碼解析

Charming本尊發表於2017-12-26

前言

在寫這篇原始碼解析到一半時發現EventBus有很多高階用法是不能直接忽略的,於是回過頭來寫了EventBus高階用法這一篇,傳送門:Android開源庫——EventBus高階用法
這篇主要通過原始碼分析來了解EventBus的實現原理,EventBus的版本為3.1.1,主要分為三部分,也就是實現的三個步驟

  1. 註冊訂閱者
  2. 事件釋出
  3. 反註冊訂閱者

話不多說,馬上開始

原始碼分析

註冊訂閱者

首先我們從EventBus註冊訂閱者的這一句入手

EventBus.getDefault().register(this);
複製程式碼

getDefault()表面意思大概是獲取一個EventBus的例項,看一下原始碼

/** 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;
}
複製程式碼

一個DoubleCheck的單例模式獲取到例項,能高效的面對併發獲取例項的情況
再看一下EventBus的構造方法

public EventBus() {
    this(DEFAULT_BUILDER);
}

EventBus(EventBusBuilder builder) {
    subscriptionsByEventType = new HashMap<>();
    typesBySubscriber = new HashMap<>();
    stickyEvents = new ConcurrentHashMap<>();
    mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
    backgroundPoster = new BackgroundPoster(this);
    asyncPoster = new AsyncPoster(this);
    indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
            builder.strictMethodVerification, builder.ignoreGeneratedIndex);
    logSubscriberExceptions = builder.logSubscriberExceptions;
    logNoSubscriberMessages = builder.logNoSubscriberMessages;
    sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
    sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
    throwSubscriberException = builder.throwSubscriberException;
    eventInheritance = builder.eventInheritance;
    executorService = builder.executorService;
}
複製程式碼

預設的構造方法又呼叫引數為EventBusBuilder的構造方法,構造出EventBus的例項,這裡使用的是預設的一個EventBusBuilder,從名字就可以看出EventBusBuilder採用的是Builder模式,個性化配置EventBus的各項引數儲存在Builder中,build時構造EventBus並將各項配置應用於EventBus

/** Builds an EventBus based on the current configuration. */
public EventBus build() {
    return new EventBus(this);
}
複製程式碼

接下來看register()

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    // 獲取訂閱者的訂閱方法並用List封裝
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        // 逐個訂閱
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
複製程式碼

register()接收引數為Object型別的訂閱者,通常也就是程式碼中Activity和Fragment的例項this。subscriberMethodFinder是EventBus的一個成員,可以看作是一個訂閱方法查詢器,是在剛剛的EventBus構造方法通過EventBusBuilder的一些引數構造出來的。呼叫findSubscriberMethods方法,傳入訂閱者的Class物件,字面意思是找出訂閱者中所有的訂閱方法,用一個List集合來接收,看看是不是

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }
    if (ignoreGeneratedIndex) {
        // 使用反射方式獲取
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
        // 使用SubscriberIndex方式獲取
        subscriberMethods = findUsingInfo(subscriberClass);
    }
    // 若訂閱者中沒有訂閱方法,則拋異常
    if (subscriberMethods.isEmpty()) {
        throw new EventBusException("Subscriber " + subscriberClass
                + " and its super classes have no public methods with the @Subscribe annotation");
    } else {
        // 快取訂閱者的訂閱方法List
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
複製程式碼

METHOD_CACHE是一個Map,以訂閱者的Class物件為key,訂閱者中的訂閱方法List為value,快取了註冊過的訂閱方法,如果有快取則返回返回快取,如果沒有則繼續往下執行。這裡看到ignoreGeneratedIndex這個屬性,意思為是否忽略生成index,是在構造SubscriberMethodFinder通過EventBusBuilder的同名屬性賦值的,預設為false,當為true時,表示以反射的方式獲取訂閱者中的訂閱方法,當為false時,則以Subscriber Index的方式獲取。接下來分別分析這兩種方式。
首先是ignoreGeneratedIndex為true時,追蹤原始碼

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
    // 建立並初始化FindState物件
    FindState findState = prepareFindState();
    // findState與subscriberClass關聯
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        // 使用反射的方式獲取單個類的訂閱方法
        findUsingReflectionInSingleClass(findState);
        // 使findState.clazz指向父類的Class,繼續獲取
        findState.moveToSuperclass();
    }
    // 返回訂閱者極其父類的訂閱方法List,並釋放資源
    return getMethodsAndRelease(findState);
}
複製程式碼

建立一個FindState物件來輔助獲取訂閱方法,這裡只看反射獲取的關鍵程式碼:findUsingReflectionInSingleClass

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的方法和static等修飾的方法
        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];
                    // 檢查eventType決定是否訂閱,通常訂閱者不能有多個eventType相同的訂閱方法
                    if (findState.checkAdd(method, eventType)) {
                        // 獲取執行緒模式
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        // 新增訂閱方法進List
                        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");
        }
    }
}
複製程式碼

程式碼也不難懂,經過修飾符、引數個數、是否有註解、和訂閱者是否有eventType相同的方法幾層條件的篩選,最終將訂閱方法新增進findState的subscriberMethods這個List中。來到這裡,不得不說EventBus真是非常強大,它不僅僅獲取當前類的訂閱方法,還會獲取它所有父類的訂閱方法

// 使findState.clazz指向父類的Class,繼續獲取
findState.moveToSuperclass();
複製程式碼

通過改變尋找狀態物件findState的clazz屬性,使之指向父類的Class,來遍歷出當前類整個家族的訂閱方法,最終獲取到所有的訂閱方法後返回List並釋放資源。
接下來就看看ignoreGeneratedIndex為false的情況

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        // 獲取當前clazz對應的SubscriberInfo
        findState.subscriberInfo = getSubscriberInfo(findState);
        if (findState.subscriberInfo != null) {
            // 通過SubscriberInfo獲取閱方法陣列
            SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
            // 逐個新增進findState.subscriberMethods
            for (SubscriberMethod subscriberMethod : array) {
                if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                    findState.subscriberMethods.add(subscriberMethod);
                }
            }
        } else {
            // 若SubscriberInfo為空,則採用反射方式獲取
            findUsingReflectionInSingleClass(findState);
        }
        findState.moveToSuperclass();
    }
複製程式碼

跟反射方式的findUsingReflection的首尾有點類似,不同的是它是通過SubscriberInfo這個類來獲取訂閱方法的,那麼SubscriberInfo物件是怎麼獲取的呢,那麼同樣只看關鍵程式碼:getSubscriberInfo

private SubscriberInfo getSubscriberInfo(FindState findState) {
    if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
        SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
        if (findState.clazz == superclassInfo.getSubscriberClass()) {
            return superclassInfo;
        }
    }
    if (subscriberInfoIndexes != null) {
        for (SubscriberInfoIndex index : subscriberInfoIndexes) {
            // 通過SubscriberIndex獲取findState.clazz對應的SubscriberInfo
            SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
            if (info != null) {
                return info;
            }
        }
    }
    return null;
}
複製程式碼

這時候主角出現了,我們看subscriberInfoIndexes,它是一個List,型別為Subscriber Index,意思訂閱者索引,也是這種方式的重要角色。追蹤它的出處

SubscriberMethodFinder(List<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification,
                       boolean ignoreGeneratedIndex) {
    this.subscriberInfoIndexes = subscriberInfoIndexes;
    this.strictMethodVerification = strictMethodVerification;
    this.ignoreGeneratedIndex = ignoreGeneratedIndex;
}
複製程式碼

是在構造SubscriberMethodFinder時賦值的,而SubscriberMethodFinder是在EventBus的構造方法中通過EventBusBuilder的引數構造的

subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
        builder.strictMethodVerification, builder.ignoreGeneratedIndex);
複製程式碼

繼續追蹤EventBusBuilder中的subscriberInfoIndexes,發現如下程式碼

/** Adds an index generated by EventBus' annotation preprocessor. */
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
    if(subscriberInfoIndexes == null) {
        subscriberInfoIndexes = new ArrayList<>();
    }
    subscriberInfoIndexes.add(index);
    return this;
}
複製程式碼

通過註釋可知,Subscriber Index是由EventBus註解處理器生成的,那麼EventBus註解處理器是什麼呢?如果你想用索引的方式來獲取訂閱方法,這時候就很有必要看一下官方文件,它教你如何使用Subscriber Index,非常重要

EventBus Subscriber Index 使用教程

看完文件,我們知道Subscriber Index是EventBus 3上的新技術,所以這裡也建議還沒學習過EventBus的可以跳過2.X之前的版本直接學習最新版本。
關於EventBus的Subscriber Index技術的特點,翻譯一下官方解釋:

It is an optional optimization to speed up initial subscriber registration.

Subscriber Index是一個可選的優化技術,用來加速初始化訂閱者註冊。

The subscriber index can be created during build time using the EventBus annotation processor. While it is not required to use an index, it is recommended on Android for best performance.

Subscriber Index在編譯時使用EventBus註解處理器建立,雖然沒有規定必須使用它,但是官方推薦使用這種方式,因為它在Android上有著最佳的效能。

回到程式碼中來,findState.subscriberInfo始終指向當前正在獲取訂閱方法的訂閱者的subscriberInfo,看關鍵程式碼

SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
複製程式碼

通過getSubscriberMethods()獲取到訂閱方法陣列,然後與反射方式類似地,逐個將訂閱方法新增進findState的subscriberMethods這個List中,返回該List並釋放資源。

// 逐個新增進findState.subscriberMethods
for (SubscriberMethod subscriberMethod : array) {
    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
        findState.subscriberMethods.add(subscriberMethod);
    }
}
複製程式碼

到此,兩種方式講解完畢。無論通過哪種方式獲取,獲取到訂閱方法List之後,接下來是真正訂閱的過程,回到register()中看程式碼

synchronized (this) {
    for (SubscriberMethod subscriberMethod : subscriberMethods) {
        subscribe(subscriber, subscriberMethod);
    }
}
複製程式碼

遍歷逐個訂閱,看subscribe方法

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    // 建立Subscription封裝訂閱者和訂閱方法資訊
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    // 根據事件型別從subscriptionsByEventType這個Map中獲取Subscription集合
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    // 若Subscription集合為空,建立並put進Map中
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        // 若集合中已包含該Subscription則拋異常
        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++) {
        // 按照優先順序插入Subscription
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }
    // typesBySubscriber與subscriptionsByEventType類似
    // 用來存放訂閱者中的事件型別
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    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();
                    // 執行設定了sticky模式的訂閱方法
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        } else {
            Object stickyEvent = stickyEvents.get(eventType);
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }
}
複製程式碼

方便起見,關鍵程式碼都附上了註釋,簡單講解一下關鍵角色:

  • subscriptionsByEventType:以事件型別為key,擁有相同事件型別的訂閱方法List為value,存放所有的訂閱方法。
  • typesBySubscriber:以訂閱者為key,訂閱者訂閱的所有事件型別List為value,存放所有的事件型別。

在這裡出現了stickyEvent(黏性事件),接下來會在事件分佈中講解它的作用,以上便是註冊訂閱者的過程。

事件釋出

一般的事件釋出方式

EventBus.getDefault().post(new UpdateUIEvent());
複製程式碼

這裡我們看post()

/** Posts the given event to the event bus. */
public void post(Object event) {
    // 獲取當前執行緒的posting狀態
    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()) {
                PostingThreadState(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}
複製程式碼

EventBus用ThreadLocal儲存每個執行緒的PostingThreadState,一個儲存了事件釋出狀態的類,當post一個事件時,新增到事件佇列末尾,等待前面的事件釋出完畢後再拿出來釋出,這裡看事件釋出的關鍵程式碼PostingThreadState()

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) {
            Log.d(TAG, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}
複製程式碼

程式碼也非常簡單,首先看eventInheritance這個屬性,是否開啟事件繼承,若是,找出釋出事件的所有父類,也就是lookupAllEventTypes(),然後遍歷每個事件型別進行釋出,若不是,則直接釋出該事件。如果需要釋出的事件沒有找到任何匹配的訂閱資訊,則釋出一個NoSubscriberEvent事件。這裡只看釋出事件的關鍵程式碼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;
}
複製程式碼

來到這裡,開始根據事件型別匹配出訂閱資訊,如果該事件有訂閱資訊,則執行postToSubscription(),釋出事件到每個訂閱者,返回true,若沒有,則發回false。繼續追蹤釋出事件到具體訂閱者的程式碼

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:在非主執行緒和釋出執行緒中訂閱。

訂閱者四種執行緒模式的特點對應的就是以上程式碼,簡單來講就是訂閱者指定了在哪個執行緒訂閱事件,無論釋出者在哪個執行緒,它都會將事件發不到訂閱者指定的執行緒。這裡我們繼續看實現訂閱者的方法

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);
    }
}
複製程式碼

到這裡我們終於看到了Java的API,有種撥開雲霧見月明的感覺

subscription.subscriberMethod.method.invoke(subscription.subscriber, event)
複製程式碼

訂閱者接收到了事件,呼叫訂閱方法,傳入釋出的事件作為引數,至此,事件釋出過程就結束了。

反註冊訂閱者

先看反註冊的程式碼

EventBus.getDefault().unregister(this);
複製程式碼

跟蹤unregister()

/** 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) {
            unsubscribeByEventType(subscriber, eventType);
        }
        typesBySubscriber.remove(subscriber);
    } else {
        Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}
複製程式碼

註冊過程我們就知道typesBySubscriber是儲存訂閱者的所有訂閱事件型別的一個Map,這裡根據訂閱者拿到訂閱事件型別List,然後逐個取消訂閱,最後typesBySubscriber移除該訂閱者,。這裡只需要關注它是如果取消訂閱的,跟蹤unsubscribeByEventType()

/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
    List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions != null) {
        int size = subscriptions.size();
        for (int i = 0; i < size; i++) {
            Subscription subscription = subscriptions.get(i);
            if (subscription.subscriber == subscriber) {
                subscription.active = false;
                subscriptions.remove(i);
                i--;
                size--;
            }
        }
    }
}
複製程式碼

subscriptionsByEventType是儲存事件型別對應訂閱資訊的Map,程式碼邏輯非常清晰,找出某事件型別的訂閱資訊List,遍歷訂閱資訊,將要取消訂閱的訂閱者和訂閱資訊封裝的訂閱者比對,如果是同一個,則說明該訂閱資訊是將要失效的,於是將該訂閱資訊移除。

到這裡,反註冊訂閱者的過程就講解完畢啦。

總結

回顧一下EventBus的三個步驟

  1. 註冊訂閱者
  2. 事件釋出
  3. 反註冊訂閱者

通過分析EventBus原始碼,發現註冊的過程最為複雜,釋出次之,反註冊最簡單,但這也是相對而言,總的來講,EventBus這個庫的原始碼沒有太多晦澀難懂的地方,算是非常好理解的,這裡不得不佩服EvenBus的作者們,整個庫的架構、邏輯設計,介面設計,小到方法和變數的命名,都顯得非常優雅易懂,相比之下,曾閱讀過的Glide的原始碼就顯得......哈哈,不過這兩個庫的程式碼量也不是同一個級別的,不能簡單地直接比較。
好了,EventBus的原始碼解析到這就結束了,想進一步瞭解EventBus的朋友可以親自去閱讀原始碼,畢竟自己親身去探索,比聽別人的理解更深。這裡借用Linus的一句話,與大家共勉

Read the fucking source code

歡迎關注

個人微信公眾號:Charming寫字的地方

Github:https://github.com/CharmingWhttps://github.com/CharmingW

感謝閱讀!

相關文章