Android開源庫——EventBus源碼解析

前言

在寫這篇源碼解析到一半時發(fā)現(xiàn)EventBus有很多高級用法是不能直接忽略的,于是回過頭來寫了EventBus高級用法這一篇叨橱,傳送門:Android開源庫——EventBus高級用法
這篇主要通過源碼分析來了解EventBus的實現(xiàn)原理建炫,EventBus的版本為3.1.1畦韭,主要分為三部分,也就是實現(xiàn)的三個步驟

  1. 注冊訂閱者
  2. 事件發(fā)布
  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的單例模式獲取到實例,能高效的面對并發(fā)獲取實例的情況
再看一下EventBus的構(gòu)造方法

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

默認(rèn)的構(gòu)造方法又調(diào)用參數(shù)為EventBusBuilder的構(gòu)造方法衍慎,構(gòu)造出EventBus的實例转唉,這里使用的是默認(rèn)的一個EventBusBuilder,從名字就可以看出EventBusBuilder采用的是Builder模式稳捆,個性化配置EventBus的各項參數(shù)保存在Builder中酝掩,build時構(gòu)造EventBus并將各項配置應(yīng)用于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()接收參數(shù)為Object類型的訂閱者,通常也就是代碼中Activity和Fragment的實例this眷柔。subscriberMethodFinder是EventBus的一個成員期虾,可以看作是一個訂閱方法查找器,是在剛剛的EventBus構(gòu)造方法通過EventBusBuilder的一些參數(shù)構(gòu)造出來的驯嘱。調(diào)用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,緩存了注冊過的訂閱方法负乡,如果有緩存則返回返回緩存牍白,如果沒有則繼續(xù)往下執(zhí)行。這里看到ignoreGeneratedIndex這個屬性抖棘,意思為是否忽略生成index茂腥,是在構(gòu)造SubscriberMethodFinder通過EventBusBuilder的同名屬性賦值的,默認(rèn)為false切省,當(dāng)為true時最岗,表示以反射的方式獲取訂閱者中的訂閱方法,當(dāng)為false時朝捆,則以Subscriber Index的方式獲取般渡。接下來分別分析這兩種方式。
首先是ignoreGeneratedIndex為true時,追蹤源碼

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
    // 創(chuàng)建并初始化FindState對象
    FindState findState = prepareFindState();
    // findState與subscriberClass關(guān)聯(lián)
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        // 使用反射的方式獲取單個類的訂閱方法
        findUsingReflectionInSingleClass(findState);
        // 使findState.clazz指向父類的Class驯用,繼續(xù)獲取
        findState.moveToSuperclass();
    }
    // 返回訂閱者極其父類的訂閱方法List脸秽,并釋放資源
    return getMethodsAndRelease(findState);
}

創(chuàng)建一個FindState對象來輔助獲取訂閱方法,這里只看反射獲取的關(guān)鍵代碼: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) {
            // 獲取訂閱方法的所有參數(shù)
            Class<?>[] parameterTypes = method.getParameterTypes();
            // 訂閱方法只能有一個參數(shù)晨汹,否則忽略
            if (parameterTypes.length == 1) {
                // 獲取注解
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                if (subscribeAnnotation != null) {
                    // 獲取第一個參數(shù)
                    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");
        }
    }
}

代碼也不難懂贷盲,經(jīng)過修飾符淘这、參數(shù)個數(shù)、是否有注解巩剖、和訂閱者是否有eventType相同的方法幾層條件的篩選铝穷,最終將訂閱方法添加進findState的subscriberMethods這個List中。來到這里佳魔,不得不說EventBus真是非常強大曙聂,它不僅僅獲取當(dāng)前類的訂閱方法,還會獲取它所有父類的訂閱方法

// 使findState.clazz指向父類的Class鞠鲜,繼續(xù)獲取
findState.moveToSuperclass();

通過改變尋找狀態(tài)對象findState的clazz屬性宁脊,使之指向父類的Class,來遍歷出當(dāng)前類整個家族的訂閱方法贤姆,最終獲取到所有的訂閱方法后返回List并釋放資源榆苞。
接下來就看看ignoreGeneratedIndex為false的情況

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        // 獲取當(dāng)前clazz對應(yīng)的SubscriberInfo
        findState.subscriberInfo = getSubscriberInfo(findState);
        if (findState.subscriberInfo != null) {
            // 通過SubscriberInfo獲取閱方法數(shù)組
            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對象是怎么獲取的呢,那么同樣只看關(guān)鍵代碼: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對應(yīng)的SubscriberInfo
            SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
            if (info != null) {
                return info;
            }
        }
    }
    return null;
}

這時候主角出現(xiàn)了碧信,我們看subscriberInfoIndexes赊琳,它是一個List,類型為Subscriber Index砰碴,意思訂閱者索引躏筏,也是這種方式的重要角色。追蹤它的出處

SubscriberMethodFinder(List<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification,
                       boolean ignoreGeneratedIndex) {
    this.subscriberInfoIndexes = subscriberInfoIndexes;
    this.strictMethodVerification = strictMethodVerification;
    this.ignoreGeneratedIndex = ignoreGeneratedIndex;
}

是在構(gòu)造SubscriberMethodFinder時賦值的呈枉,而SubscriberMethodFinder是在EventBus的構(gòu)造方法中通過EventBusBuilder的參數(shù)構(gòu)造的

subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
        builder.strictMethodVerification, builder.ignoreGeneratedIndex);

繼續(xù)追蹤EventBusBuilder中的subscriberInfoIndexes寸士,發(fā)現(xiàn)如下代碼

/** 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上的新技術(shù)荧飞,所以這里也建議還沒學(xué)習(xí)過EventBus的可以跳過2.X之前的版本直接學(xué)習(xí)最新版本凡人。
關(guān)于EventBus的Subscriber Index技術(shù)的特點名党,翻譯一下官方解釋:

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

Subscriber Index是一個可選的優(yōu)化技術(shù),用來加速初始化訂閱者注冊挠轴。

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注解處理器創(chuàng)建传睹,雖然沒有規(guī)定必須使用它,但是官方推薦使用這種方式岸晦,因為它在Android上有著最佳的性能欧啤。

回到代碼中來,findState.subscriberInfo始終指向當(dāng)前正在獲取訂閱方法的訂閱者的subscriberInfo启上,看關(guān)鍵代碼

SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();

通過getSubscriberMethods()獲取到訂閱方法數(shù)組邢隧,然后與反射方式類似地,逐個將訂閱方法添加進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;
    // 創(chuàng)建Subscription封裝訂閱者和訂閱方法信息
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    // 根據(jù)事件類型從subscriptionsByEventType這個Map中獲取Subscription集合
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    // 若Subscription集合為空,創(chuàng)建并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++) {
        // 按照優(yōu)先級插入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);
    // 訂閱方法是否設(shè)置黏性模式
    if (subscriberMethod.sticky) {
        // 是否設(shè)置了事件繼承
        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();
                // 判斷當(dāng)前事件類型是否為黏性事件或者其子類
                if (eventType.isAssignableFrom(candidateEventType)) {
                    Object stickyEvent = entry.getValue();
                    // 執(zhí)行設(shè)置了sticky模式的訂閱方法
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        } else {
            Object stickyEvent = stickyEvents.get(eventType);
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }
}

方便起見发魄,關(guān)鍵代碼都附上了注釋盹牧,簡單講解一下關(guān)鍵角色:

  • subscriptionsByEventType:以事件類型為key,擁有相同事件類型的訂閱方法List為value励幼,存放所有的訂閱方法汰寓。
  • typesBySubscriber:以訂閱者為key,訂閱者訂閱的所有事件類型List為value苹粟,存放所有的事件類型有滑。

在這里出現(xiàn)了stickyEvent(黏性事件),接下來會在事件分布中講解它的作用嵌削,以上便是注冊訂閱者的過程毛好。

事件發(fā)布

一般的事件發(fā)布方式

EventBus.getDefault().post(new UpdateUIEvent());

這里我們看post()

/** Posts the given event to the event bus. */
public void post(Object event) {
    // 獲取當(dāng)前線程的posting狀態(tài)
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    // 將事件添加進當(dāng)前線程的事件隊列
    eventQueue.add(event);
    // 判斷當(dāng)前線程是否正在發(fā)布事件
    if (!postingState.isPosting) {
        postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
        postingState.isPosting = true;
        // 取消發(fā)布狀態(tài)沒有重置,拋異常
        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苛秕,一個存儲了事件發(fā)布狀態(tài)的類肌访,當(dāng)post一個事件時,添加到事件隊列末尾艇劫,等待前面的事件發(fā)布完畢后再拿出來發(fā)布吼驶,這里看事件發(fā)布的關(guān)鍵代碼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);
            // 發(fā)布事件
            subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
        }
    } else {
        // 發(fā)布事件
        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這個屬性,是否開啟事件繼承蟹演,若是风钻,找出發(fā)布事件的所有父類,也就是lookupAllEventTypes()酒请,然后遍歷每個事件類型進行發(fā)布骡技,若不是,則直接發(fā)布該事件羞反。如果需要發(fā)布的事件沒有找到任何匹配的訂閱信息布朦,則發(fā)布一個NoSubscriberEvent事件。這里只看發(fā)布事件的關(guān)鍵代碼postSingleEventForEventType()

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        // 根據(jù)事件類型找出相關(guān)的訂閱信息
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            try {
                // 發(fā)布事件到具體的訂閱者
                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;
}

來到這里苟弛,開始根據(jù)事件類型匹配出訂閱信息喝滞,如果該事件有訂閱信息阁将,則執(zhí)行postToSubscription()膏秫,發(fā)布事件到每個訂閱者,返回true做盅,若沒有缤削,則發(fā)回false。繼續(xù)追蹤發(fā)布事件到具體訂閱者的代碼

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        // 訂閱線程跟隨發(fā)布線程
        case POSTING:
            // 訂閱線程和發(fā)布線程相同吹榴,直接訂閱
            invokeSubscriber(subscription, event);
            break;
        // 訂閱線程為主線程
        case MAIN:
            if (isMainThread) {
                // 發(fā)布線程和訂閱線程都是主線程亭敢,直接訂閱
                invokeSubscriber(subscription, event);
            } else {
                // 發(fā)布線程不是主線程,訂閱線程切換到主線程訂閱
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        // 訂閱線程為后臺線程
        case BACKGROUND:
            if (isMainThread) {
                // 發(fā)布線程為主線程图筹,切換到后臺線程訂閱
                backgroundPoster.enqueue(subscription, event);
            } else {
                // 發(fā)布線程不為主線程帅刀,直接訂閱
                invokeSubscriber(subscription, event);
            }
            break;
        // 訂閱線程為異步線程
        case ASYNC:
            // 使用線程池線程訂閱
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

看到四種線程模式,我們回顧一下上一篇的內(nèi)容

  • POSTING:事件發(fā)布在什么線程远剩,就在什么線程訂閱扣溺。
  • MAIN:無論事件在什么線程發(fā)布,都在主線程訂閱瓜晤。
  • BACKGROUND:如果發(fā)布的線程不是主線程锥余,則在該線程訂閱,如果是主線程痢掠,則使用一個單獨的后臺線程訂閱驱犹。
  • ASYNC:在非主線程和發(fā)布線程中訂閱。

訂閱者四種線程模式的特點對應(yīng)的就是以上代碼足画,簡單來講就是訂閱者指定了在哪個線程訂閱事件雄驹,無論發(fā)布者在哪個線程,它都會將事件發(fā)不到訂閱者指定的線程淹辞。這里我們繼續(xù)看實現(xiàn)訂閱者的方法

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)

訂閱者接收到了事件,調(diào)用訂閱方法,傳入發(fā)布的事件作為參數(shù)彬向,至此兼贡,事件發(fā)布過程就結(jié)束了。

反注冊訂閱者

先看反注冊的代碼

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娃胆,這里根據(jù)訂閱者拿到訂閱事件類型List遍希,然后逐個取消訂閱,最后typesBySubscriber移除該訂閱者,里烦。這里只需要關(guān)注它是如果取消訂閱的凿蒜,跟蹤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是存儲事件類型對應(yīng)訂閱信息的Map,代碼邏輯非常清晰胁黑,找出某事件類型的訂閱信息List废封,遍歷訂閱信息,將要取消訂閱的訂閱者和訂閱信息封裝的訂閱者比對丧蘸,如果是同一個漂洋,則說明該訂閱信息是將要失效的,于是將該訂閱信息移除力喷。

到這里刽漂,反注冊訂閱者的過程就講解完畢啦。

總結(jié)

回顧一下EventBus的三個步驟

  1. 注冊訂閱者
  2. 事件發(fā)布
  3. 反注冊訂閱者

通過分析EventBus源碼弟孟,發(fā)現(xiàn)注冊的過程最為復(fù)雜贝咙,發(fā)布次之,反注冊最簡單拂募,但這也是相對而言庭猩,總的來講,EventBus這個庫的源碼沒有太多晦澀難懂的地方陈症,算是非常好理解的蔼水,這里不得不佩服EvenBus的作者們,整個庫的架構(gòu)爬凑、邏輯設(shè)計徙缴,接口設(shè)計,小到方法和變量的命名嘁信,都顯得非常優(yōu)雅易懂于样,相比之下,曾閱讀過的Glide的源碼就顯得......哈哈潘靖,不過這兩個庫的代碼量也不是同一個級別的穿剖,不能簡單地直接比較。
好了卦溢,EventBus的源碼解析到這就結(jié)束了糊余,想進一步了解EventBus的朋友可以親自去閱讀源碼秀又,畢竟自己親身去探索,比聽別人的理解更深贬芥。這里借用Linus的一句話吐辙,與大家共勉

Read the fucking source code

歡迎關(guān)注

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

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

感謝閱讀!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蘸劈,一起剝皮案震驚了整個濱河市奶赔,隨后出現(xiàn)的幾起案子舍败,更是在濱河造成了極大的恐慌抱环,老刑警劉巖伤哺,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異棒掠,居然都是意外死亡孵构,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門烟很,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颈墅,“玉大人,你說我怎么就攤上這事溯职【眩” “怎么了帽哑?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵谜酒,是天一觀的道長。 經(jīng)常有香客問我妻枕,道長僻族,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任屡谐,我火速辦了婚禮述么,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘愕掏。我一直安慰自己度秘,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布饵撑。 她就那樣靜靜地躺著剑梳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪滑潘。 梳的紋絲不亂的頭發(fā)上垢乙,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音语卤,去河邊找鬼追逮。 笑死酪刀,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的钮孵。 我是一名探鬼主播骂倘,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼巴席!你這毒婦竟也來了稠茂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤情妖,失蹤者是張志新(化名)和其女友劉穎睬关,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毡证,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡电爹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了料睛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丐箩。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖恤煞,靈堂內(nèi)的尸體忽然破棺而出屎勘,到底是詐尸還是另有隱情,我是刑警寧澤居扒,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布概漱,位于F島的核電站,受9級特大地震影響喜喂,放射性物質(zhì)發(fā)生泄漏瓤摧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一玉吁、第九天 我趴在偏房一處隱蔽的房頂上張望照弥。 院中可真熱鬧,春花似錦进副、人聲如沸这揣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽给赞。三九已至,卻和暖如春鸥昏,著一層夾襖步出監(jiān)牢的瞬間塞俱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工吏垮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留障涯,地道東北人罐旗。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像唯蝶,于是被迫代替她去往敵國和親九秀。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 博文出處:EventBus源碼解析粘我,歡迎大家關(guān)注我的博客鼓蜒,謝謝! 0001B 時近年末征字,但是也沒閑著都弹。最近正好在看...
    俞其榮閱讀 1,294評論 1 16
  • EventBus用法及源碼解析目錄介紹1.EventBus簡介1.1 EventBus的三要素1.2 EventB...
    楊充211閱讀 1,884評論 0 4
  • 簡單的使用 EventBus是greenrobot在Android平臺發(fā)布的一款以訂閱——發(fā)布模式為核心的開源庫。...
    最最最最醉人閱讀 756評論 0 13
  • 項目到了一定階段會出現(xiàn)一種甜蜜的負(fù)擔(dān):業(yè)務(wù)的不斷發(fā)展與人員的流動性越來越大匙姜,代碼維護與測試回歸流程越來越繁瑣畅厢。這個...
    fdacc6a1e764閱讀 3,163評論 0 6
  • 九月框杜,又是一年開學(xué)季。 也不知道從什么時候開始袖肥,面對所謂的開學(xué)咪辱,我越來越能體會到那份屬于開學(xué)季所夾雜的夢想和牽掛的...
    余七澤閱讀 688評論 11 9