EventBus源碼解析(一)—訂閱過程

1.EventBus源碼解析(一)—訂閱過程
2.EventBus源碼解析(二)—發(fā)布事件和注銷流程
3.EventBus源碼解析(三)—進階源碼

前言

最近發(fā)現(xiàn)EventBus用起來是真的方便凸克,本來對于EventBus我對于這個框架的源碼的閱讀的優(yōu)先級是比較低的议蟆,因為這個框架不像OkHttp,Glide那樣層層嵌套萎战,步步深入咐容,基本上有一定基礎(chǔ)的人對于EventBus的原理都會有一定的理解——反射。但是最近突然發(fā)現(xiàn)僅僅是了解這些是不夠的蚂维,如果細想一下會發(fā)現(xiàn)戳粒,EventBus的實現(xiàn)還是有許多值得學(xué)習(xí)的地方的,我來說一下讓我疑惑的問題以至于想讓我去追究EventBus底層源碼的問題吧鸟雏。

1.EventBus在接受事件的時候如何區(qū)分多個相同的Activity享郊?
2.EventBus事件的接收在父類和子類之間是什么規(guī)則?
3.EventBus對于多態(tài)類型的事件是如何識別的孝鹊?
4.EventBus只能用于Activity和Fragment之間嗎炊琉?
5.EventBus可以創(chuàng)建多個實例嗎?互相干擾嗎又活?

再沒有看EventBus源碼之前苔咪,上面這些問題,我是沒法回答的柳骄。意識到看源碼的必要性团赏,我提高的EventBus源碼閱讀的優(yōu)先級,本篇博客主要從分析注冊流程耐薯。

源碼分析

注冊流程

EventBus.getDefault().register(this);

EventBus的使用本篇博客就不講解了舔清,注冊的代碼很簡單,一行遍完成曲初,接下來詳細看看實現(xiàn)源碼体谒。

1.getDefault()

public static EventBus getDefault() {
        //常規(guī)的雙重鎖單例模式,特殊的是構(gòu)造方法是公有的臼婆,也就是可以創(chuàng)建多個EventBus,互相不干擾
        EventBus instance = defaultInstance;
        if (instance == null) {
            synchronized (EventBus.class) {
                instance = EventBus.defaultInstance;
                if (instance == null) {
                    instance = EventBus.defaultInstance = new EventBus();
                }
            }
        }
        return instance;
    }

對于getDefault()不出意料果然是常規(guī)的雙重鎖的單例模式抒痒,但是這里有一個地方和平常的單例模式不同,平常的單例模式構(gòu)造方法往往是私有的颁褂,用于保證全局唯一故响,但是這里我們看一下EventBus的構(gòu)造函數(shù)傀广。

/**
     * Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
     * central bus, consider {@link #getDefault()}.
     */
    public EventBus() {
        this(DEFAULT_BUILDER);
    }

這里會發(fā)現(xiàn)EventBus的構(gòu)造方法是公有的,也就是說我們自己可以自己new一個EventBus的實例彩届,當(dāng)然EventBus也提供了EventBusBuilder供我們構(gòu)造使用伪冰。這里特意把構(gòu)造方法的注釋放了上來,大概翻譯一下吧惨缆。

創(chuàng)建一個EventBus實例糜值,每一個實例的事件傳遞都在自己的實例范圍,如果想統(tǒng)一管理坯墨,可以使用getDefault()方法寂汇。
這里通過注釋我們就可以解釋第五個問題了,當(dāng)然只是從注釋層面捣染,EventBus是可以創(chuàng)建多個實例的骄瓣,并且多個實例的發(fā)送的事件是互不干擾的

2.register(Object subscriber)

public void register(Object subscriber) {
        //獲得訂閱者對應(yīng)的Class,MainActivity.class
        Class<?> subscriberClass = subscriber.getClass();
        //找到所有的訂閱的方法
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

這里開始看注冊的過程,可以看到往往我們注冊時傳入的是this,所以這里首先調(diào)用getClass(),獲得的就是MainActivity.class(舉例)耍攘,獲得完這個class對象后榕栏,會調(diào)用findSubscriberMethods方法來找到該Class中所有的訂閱方法。

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        //Cache中存在則直接從Cache中獲取
        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;
        }
    }

這里首先從緩存中獲取蕾各。METHOD_CACHE對應(yīng)的是在聲明的時候就初始化完成了,是一個ConcurrentHashMap

private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();

可以看到這里的key是class對象扒磁,那么我們可以發(fā)現(xiàn),如果已經(jīng)通過反射得到的訂閱方法的類式曲,再次注冊的時候妨托,不會再次通過反射獲取。
下面可以看到對于ignoreGeneratedIndex變量的判斷吝羞,這個變量默認是false兰伤,也就是默認的時候我們是通過編譯期注解來實現(xiàn)映射表的構(gòu)建的,本篇博客主要分析通過運行時反射來回去訂閱的方法的钧排,對于索引的方式敦腔,后面的博客如果有時間會考慮分析源碼。

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
        //緩存中獲取恨溜,默認大小為4都數(shù)組符衔,沒有就new
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            //findState保存注冊的訂閱者中的方法相關(guān)信息
            findUsingReflectionInSingleClass(findState);
            //向上繼續(xù)遍歷
            findState.moveToSuperclass();
        }
        //返回方法集
        return getMethodsAndRelease(findState);
    }

這里可以看到首先獲取FindState對象,使用了prepareFindState方法

private static final int POOL_SIZE = 4;
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
private FindState prepareFindState() {
        //有點簡單粗暴的意思糟袁。柏腻。。
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                FindState state = FIND_STATE_POOL[i];
                if (state != null) {
                    FIND_STATE_POOL[i] = null;
                    return state;
                }
            }
        }
        return new FindState();
    }

可以看到默認是有一個大小為4的數(shù)組用于緩存FindState對象的系吭,由于FindState對象比較大,所以EventBus考慮使用緩存進行復(fù)用颗品,當(dāng)然這個緩存實現(xiàn)起來也比較簡單粗暴~
接下來是一個循環(huán)肯尺。

while (findState.clazz != null) {
            //findState保存注冊的訂閱者中的方法相關(guān)信息
            findUsingReflectionInSingleClass(findState);
            //向上繼續(xù)遍歷
            findState.moveToSuperclass();
        }

可以看到沃缘,此時我們的findState.clazz是MainActivity.class(舉例),我們這里先看一下遍歷的規(guī)則则吟,再來看遍歷里干了些什么槐臀,首先這里的判斷依據(jù)是根據(jù)findState.clazz!=null.每次執(zhí)行完findUsingReflectionInSingleClass(findState);方法都會執(zhí)行findState.moveToSuperclass方法。

void moveToSuperclass() {
            //如果配置忽略父類方法則不檢查父類方法
            if (skipSuperClasses) {
                clazz = null;
            } else {
                clazz = clazz.getSuperclass();
                String clazzName = clazz.getName();
                /** Skip system classes, this just degrades performance. */
                if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
                    clazz = null;
                }
            }
        }

這里可以看到氓仲,方法的作用就是向上遍歷父Class水慨,可以通過配置不向上遍歷父Class,但是默認是會先檢查父Class,而且這里需要注意的一個點敬扛,向上遍歷的終點是檢查到Class是以“java.”"javax.""android."開頭的晰洒,也就是說對于系統(tǒng)源碼內(nèi)的class是不會檢查的,這時class變成null啥箭,整個循環(huán)也就結(jié)束了谍珊。所以這里又可以解釋我們的一個疑惑,默認EventBus是會檢查父類中的訂閱方法的急侥。


private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            //通過注釋其實也可以看到EventBus團隊對于反射性能都考慮砌滞,所以用getDeclaredMethods而不是getMethods
            //獲取都不包括父類和接口都方法,包括私有都坏怪,
            //疑問點1
            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,abstract,生成索引時會出問題
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                //獲取方法都參數(shù)
                Class<?>[] parameterTypes = method.getParameterTypes();
                //參數(shù)個數(shù)=1
                if (parameterTypes.length == 1) {
                    //方法是用@Subscribe注解
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        //獲取第一個參數(shù)都Class,也就是事件類都class
                        Class<?> eventType = parameterTypes[0];
                        // 檢查eventType決定是否訂閱铝宵,通常訂閱者不能有多個eventType相同的訂閱方法
                        //疑問點2
                        if (findState.checkAdd(method, eventType)) {
                            //獲得Thread類型
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            //ArrayList加入一個SubscriberMethod對象
                            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");
            }
        }
    }

接下來看到這個獲得訂閱的方法打掘。這里注釋寫的都比較全了這里有幾個疑問點是需要我們了解的:

疑問點1

可以看到這里使用了getDeclaredMethods而沒有使用getMethods,這里通過注釋我們可以看到捉超,這里EventBus團隊出于對于反射性能的考慮胧卤,使用getDeclaredMethods代替getMethods方法,這里可能有疑問了拼岳,這樣使用為什么能提高性能枝誊?
首先我們來大概解釋一下兩個方法的區(qū)別吧:

public Method[] getMethods()返回某個類的所有公用(public)方法包括其繼承類的公用方法,當(dāng)然也包括它所實現(xiàn)接口的方法惜纸。
public Method[] getDeclaredMethods()對象表示的類或接口聲明的所有方法叶撒,包括公共、保護耐版、默認(包)訪問和私有方法祠够,但不包括繼承的方
法。當(dāng)然也包括它所實現(xiàn)接口的方法粪牲。

起初我也在疑惑為什么這樣能夠提升性能古瓤,直達看到了一篇博客,這篇博客也是對EventBus的源碼的講解,中間提到了的關(guān)于這里性能提升的講解落君,這里也是EventBus在完善一個issue的解決方案穿香。這里大概解釋一下:
可以看到這兩個方法比較顯著的區(qū)別是,getDeclaredMethods只會獲取當(dāng)前類的所有方法绎速,而getMethods會獲取所有公用方法皮获,包括繼承的。而前面也提到纹冤,EventBus在查找完這里后洒宝,會調(diào)用findState.moveToSuperclass(),向上繼續(xù)查詢父類的方法萌京,所以這里使用getMethods會重復(fù)查詢父類的方法雁歌。

疑問點2

findState.checkAdd(method, eventType)這層檢查是什么作用的?
這里提出兩個特殊場景(一般應(yīng)該也不會有人這樣寫吧):

1.一個類中存在兩個方法名不同枫夺,但是是同一個Event類型的訂閱方法将宪,EventBus會怎樣處理?
2.子類和父類存在相同方法名和Event類型的訂閱方法橡庞,EventBus會怎樣處理较坛?

這個方法的作用就是處理上面這兩種情況的,所以還是需要我們追究一下這里的源碼實現(xiàn):

boolean checkAdd(Method method, Class<?> eventType) {
            // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
            // Usually a subscriber doesn't have methods listening to the same event type.
            //HashMap,一般一個Event只有一個方法
            Object existing = anyMethodByEventType.put(eventType, method);
            if (existing == null) {
                    //不存在該參數(shù)類型的方法扒最,直接添加到訂閱者
                return true;
            } else {
                if (existing instanceof Method) {
                    //存在相同的參數(shù)類型丑勤,代表參數(shù)類型相同,但可能方法名不同吧趣,或者方法名也相同(父類中的被重寫的方法)
                    if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                        // Paranoia check
                        throw new IllegalStateException();
                    }
                    // Put any non-Method object to "consume" the existing Method
                    anyMethodByEventType.put(eventType, this);
                }
                return checkAddWithMethodSignature(method, eventType);
            }
        }

這里可以看到anyMethodByEventType是一個HashMap,如果當(dāng)前Event的類型是原來沒有過的法竞,直接返回true,加入到anyMethodByEventType中强挫。而當(dāng)這個參數(shù)類型已經(jīng)被加入過了岔霸,存在兩種情況,也就是剛才已經(jīng)提到的兩種情況俯渤,代表參數(shù)類型相同呆细,但可能方法名不同,或者方法名也相同(父類中的被重寫的方法)八匠,這時就要進行checkAddWithMethodSignature方法的檢查絮爷。

private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
            methodKeyBuilder.setLength(0);
            methodKeyBuilder.append(method.getName());
            methodKeyBuilder.append('>').append(eventType.getName());

            String methodKey = methodKeyBuilder.toString();
            Class<?> methodClass = method.getDeclaringClass();
            Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
            if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
            //methodClassOld = null,說明時方法名不同的情況梨树,直接返回true坑夯,會加入訂閱。
                //返回true
                // Only add if not already found in a sub class
                return true;
            } else {
            //methodClass一定是methodClassOld的父類抡四,因為是向上遍歷的柜蜈,所以父類的方法不會被加入訂閱
                // Revert the put, old class is further down the class hierarchy
                subscriberClassByMethodKey.put(methodKey, methodClassOld);
                return false;
            }
        }

可以看到這里同樣是一個HashMap類型的subscriberClassByMethodKey仗谆,而key值對應(yīng)的就是以“方法名>EventType名”。

Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);

第一種情況跨释,一個類中存在兩個方法名不同胸私,但是是同一個Event類型的訂閱方法,這時由于方法名不同鳖谈,所有key值不同,所有每次methodClassOld = null阔涉,所以接下來的判斷條件里缆娃,會直接返回true。加入這個訂閱方法瑰排。

第二種情況贯要,子類和父類存在相同方法名和Event類型的訂閱方法(也就是重寫),這時由于方法名相同椭住,Event類型也相同崇渗,所以每次methodClassOld != null,這時就要看第二個判斷條件京郑,methodClassOld.isAssignableFrom(methodClass),這里我們就要明白isAssignableFrom函數(shù)的作用宅广。

class1.isAssignableFrom(class2) class2是不是class1的子類或者子接口

這里我們就會發(fā)現(xiàn)在這種情況下,methodClassOld一定是methodClass的子類些举。因為從前面的分析我們知道跟狱,EventBus是從子類向上遍歷的過程,所以先加入的肯定是子類的方法户魏,后加入的肯定是父類的方法驶臊,所以methodClassOld.isAssignableFrom(methodClass),這個判斷肯定是false叼丑,所以最終我們在這種情況下會返回false关翎。最終也就是說明子類重寫的方法會加入訂閱,但是父類的被重寫的方法不會被加入訂閱鸠信。

向上回溯到register方法

public void register(Object subscriber) {
        //獲得訂閱者對應(yīng)的Class,MainActivity.class
        Class<?> subscriberClass = subscriber.getClass();
        //找到所有的訂閱的方法
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

到這里我們已經(jīng)分析完subscriberMethodFinder.findSubscriberMethods(subscriberClass)中反射到方式獲取所有的訂閱方法了纵寝。可以看到症副,在獲得list后店雅,會進行一次遍歷。執(zhí)行subscribe方法贞铣。

// Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        //每次都會new了一個Subscription對象闹啦,subsciber代表我們的訂閱者MainActivity.class, subscriberMethod代表其中的一個訂閱的方法。
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //判斷是否有以Event.class為key
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        //為null說明不存在該參數(shù)類型的方法沒有被訂閱過
        if (subscriptions == null) {
            //new 一個
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            //subscriptions不為null的情況是指存在多個訂閱這個Event的類辕坝。
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

        int size = subscriptions.size();
        //如果長度不為0代表該事件有大于一個訂閱者窍奋,或一個訂閱中中有多個訂閱方法訂閱這個Event,相同參數(shù)名的方法
        for (int i = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                //按優(yōu)先級加入
                subscriptions.add(i, newSubscription);
                break;
            }
        }

        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

        //粘性事件相關(guān),后面博客會分析
        //下面是粘性事件的代碼
    }

這里就要提到EventBus中我認為最為重要的兩個Map中的第一個Map——subscriptionsByEventType琳袄,到后來看完EventBus的源碼后你會發(fā)現(xiàn)理解這兩個Map基本上就理解了EventBus的基本原理江场。

首先來看第一個Map的數(shù)據(jù)結(jié)構(gòu)。

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
EventBus(EventBusBuilder builder) {
        subscriptionsByEventType = new HashMap<>();
    }

可以看到這里map是以class為key,CopyOnWriteArrayList<Subscription>為value的HashMap,這里要強調(diào)一下是HashMap窖逗,后面會解釋為什么要強調(diào)址否。(HashMap的底層原理?)
接下來我們根據(jù)上面的源碼來理解這個Map的作用碎紊。

CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);

首先嘗試從subscriptionsByEventType中嘗試是否已經(jīng)存在過這個EventType.

if (subscriptions == null) {
            //new 一個
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            //subscriptions不為null的情況是指方法名不同佑附,但是參數(shù)類型相同。也就是剛才提到的兩種情況中的第一種
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

當(dāng)不存在仗考,處理就比較簡單音同,new一個CopyOnWriteArrayList,并且在subscriptionsByEventType中加入一條數(shù)據(jù)秃嗜。
當(dāng)存在這個EventType权均,則這時就要判斷一下subscriptions.contains(newSubscription)是否已經(jīng)注冊過這個類型。這里我們要詳細理解一下锅锨。
如何判斷一個類已經(jīng)注冊過了叽赊。

//每次都會new了一個Subscription對象,subsciber代表我們的訂閱者MainActivity.class, subscriberMethod代表其中的一個訂閱的方法橡类。
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

而我們知道subscriptions是一個CopyOnWriteArrayList蛇尚。這時我們就要看一下CopyOnWriteArrayList的源碼了,contain()最后會走到indexOf()方法顾画。

public boolean contains(Object var1) {
        Object[] var2 = this.getArray();
        return indexOf(var1, var2, 0, var2.length) >= 0;
    }
//var0 是參數(shù)
//var1 是CopyOnWriteArrayList內(nèi)部的數(shù)組
//var2 是0
//var3 是數(shù)組的長度
private static int indexOf(Object var0, Object[] var1, int var2, int var3) {
        int var4;
        if(var0 == null) {
            for(var4 = var2; var4 < var3; ++var4) {
                if(var1[var4] == null) {
                    return var4;
                }
            }
        } else {
            for(var4 = var2; var4 < var3; ++var4) {
            //是通過equal判斷
                if(var0.equals(var1[var4])) {
                    return var4;
                }
            }
        }

        return -1;
    }

這里
可以看到取劫,如果判斷這個是否已經(jīng)訂閱過了,最后會通過equal方法判斷研侣,哪理所當(dāng)然Subscription肯定重寫了equal方法谱邪。

@Override
    public boolean equals(Object other) {
        if (other instanceof Subscription) {
            Subscription otherSubscription = (Subscription) other;
            //==比較的是內(nèi)存地址,所以如果兩個類地址不同庶诡,也不相同(棧內(nèi)存在兩個MainActivity),
            //是否存在相同的訂閱類名惦银,方法名,參數(shù)類型名(同一個Activity中存在兩個不同的方法訂閱這個Event)末誓。
            return subscriber == otherSubscription.subscriber
                    && subscriberMethod.equals(otherSubscription.subscriberMethod);
        } else {
            return false;
        }
    }

對于兩個
最終這里還會比較SubscriberMethod重寫的equal方法扯俱,最后會比較是否存在相同的訂閱類名,方法名喇澡,參數(shù)類型名迅栅。這里其實就是我們剛才討論的兩種特殊情況中的第一種晴玖,當(dāng)存在兩個EventType相同读存,但是方法名不同的情況为流。如果一個類中存在多個方法名相同,參數(shù)類型相同的方法让簿,則會拋異常敬察。

int size = subscriptions.size();
//如果長度不為0代表有大于1個的不同訂閱者,或者方法名尔当,相同參數(shù)名的方法
        for (int i = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                //按優(yōu)先級加入
                subscriptions.add(i, newSubscription);
                break;
            }
        }

當(dāng)沒有拋異常莲祸,則會按照優(yōu)先級進行加入,若沒有定義優(yōu)先級椭迎,則末尾加入虫给。到這對于第一個Map我們其實已經(jīng)有了一個比較深刻的理解

第一個Map的作用:保存對于一個Event所有的訂閱者和訂閱方法,
這里就放上一張圖吧侠碧,個人感覺非常有用。

subscriptionsByEventType
//維持當(dāng)前訂閱者訂閱的所有Event
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

緊接著缠黍,我們會看到EventBus中另一個重要的Map-typesBySubscriber,和剛才一樣弄兜,看一下數(shù)據(jù)結(jié)構(gòu)。

private final Map<Object, List<Class<?>>> typesBySubscriber;

typesBySubscriber = new HashMap<>();

不出意外瓷式,還是HashMap,這里的邏輯就比較好理解了贸典,key對應(yīng)的是訂閱者類型,Value對應(yīng)的是一個List<EventType.class>廊驼。

第二個Map的作用:保存訂閱者和訂閱者中的所有的訂閱Event。

typesBySubscriber

疑惑

這里其實就要解釋我們的一個疑惑了妒挎,當(dāng)棧內(nèi)存在多個相同的MainActivity時绳锅,Eventbus如何訂閱的酝掩?

這里就和我們兩個Map有關(guān)了,首先第一個Map中對于是否重復(fù)訂閱使用的contain函數(shù)進行判斷期虾,最后包含==的判斷原朝,也就是內(nèi)存地址的判斷,不同的>Activity對象镶苞,內(nèi)存地址肯定不同。

其次第二個Map,是一個HashMap宾尚,key就是我們的MainActivity谢澈,是一個Object類型御板,那么多個MainActivity時,加入第二個HashMap怠肋,為什么能區(qū)分哪?因為hashCode不同笙各,根據(jù)HashMap的原理,不同的MainActivity的hashcode不同数尿,當(dāng)然可以都加入了惶楼。

后面的代碼就是粘性事件相關(guān)的代碼邏輯了,后面的博客我們會分析歼捐。

總結(jié)

這里我們來總結(jié)一下注冊的流程:

1.獲取訂閱類里的所有的訂閱的方法,其中訂閱類父類的方法也會被記錄贷盲,重寫的方法不會被記錄剥扣,方法名不同,參數(shù)類型相同的方法會被記錄
2.填充兩個Map朦乏,第一個Map記錄訂閱了Event的所有訂閱者和其方法,第二個Map記錄了所有的訂閱者和其訂閱的所有Event吃引。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末刽锤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子并思,更是在濱河造成了極大的恐慌,老刑警劉巖弄砍,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異音婶,居然都是意外死亡,警方通過查閱死者的電腦和手機衣式,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門碴卧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人住册,你說我怎么就攤上這事∮桑” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長兑巾。 經(jīng)常有香客問我,道長帅掘,這世上最難降的妖魔是什么堂油? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮府框,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘院峡。我一直安慰自己,他們只是感情好系宜,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盹牧,像睡著了一般励幼。 火紅的嫁衣襯著肌膚如雪口柳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天六水,我揣著相機與錄音辣卒,去河邊找鬼。 笑死荣茫,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的港准。 我是一名探鬼主播咧欣,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼魄咕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起毛萌,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤喝滞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后右遭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡言蛇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年宵距,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片婿斥。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖民宿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情活鹰,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布着绷,位于F島的核電站锌云,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏彬向。R本人自食惡果不足惜攻冷,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望等曼。 院中可真熱鬧,春花似錦、人聲如沸篙程。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽氮发。三九已至,卻和暖如春仇祭,著一層夾襖步出監(jiān)牢的瞬間颈畸,已是汗流浹背没讲。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工礁苗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人试伙。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓疏叨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子酱吝,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

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

  • 最近在項目中使用了EventBus(3.0)既绕,覺得非常好用涮坐,于是就看了一些關(guān)于EventBus源碼分析的文章,現(xiàn)在...
    shenhuniurou閱讀 1,494評論 0 4
  • 前言 在上一篇文章:EventBus 3.0初探: 入門使用及其使用 完全解析中疲扎,筆者為大家介紹了EventBus...
    丶藍天白云夢閱讀 15,818評論 21 128
  • 博文出處:EventBus源碼解析椒丧,歡迎大家關(guān)注我的博客救巷,謝謝壶熏! 0001B 時近年末浦译,但是也沒閑著。最近正好在看...
    俞其榮閱讀 1,298評論 1 16
  • EventBus源碼分析(一) EventBus官方介紹為一個為Android系統(tǒng)優(yōu)化的事件訂閱總線帽哑,它不僅可以很...
    蕉下孤客閱讀 3,979評論 4 42
  • 我希望你晚上喝著我煮的粥叹俏,早上吃到我用果醬畫著笑臉的面包時,心里是幸福的佳头。 我希望你凌晨下班回來的時候,我是醒著的...
    克克閱讀 557評論 5 9