EventBus源碼解析(一)EventBus事件訂閱

前言

說起Eventbus,相信不少同學(xué)都已經(jīng)使用過晕窑。之前一直聽別人提起,但是我一直沒有使用過幕屹。前段時間在做一個IMdemo的時候用上了,原來模塊間的通信可以這么方便望拖,遂勾起了我的興趣,決定好好研究一番说敏。那么它的用法和好處我就不介紹了,直接進(jìn)入主題盔沫。

正題

本篇文章解析EventBus注冊的源碼。


EventBus注冊

可以看到EventBus使用的單例模式創(chuàng)建的實例架诞,但是EventBus的構(gòu)造函數(shù)卻不是私有的(這樣就可以自己new個EventBus對象)干茉,讓我們看看源碼谴忧。

  public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }
    /**
     * 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);
     }

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

原來因為是要初始化一些成員變量,如果把構(gòu)造器設(shè)為私有那么就不能在構(gòu)造函數(shù)中初始化這些成員變量委造。而且源碼的注釋寫的很清楚實例化EventBus使用getDefault()方法。
有些時候我們想要自己去配置一個EventBusBuilder昏兆,但是源碼中沒有提供可以把EventBusBuilder作為參數(shù)傳遞給EventBus然后創(chuàng)建EventBus實例的方法。那么如何創(chuàng)建一個我們自己定義的EventBusBuilder實例呢妇穴?這時候需要使用另一種創(chuàng)建EventBus實例的方法了爬虱。源碼給我們提供了一個得到EventBusBuilder的方法

public static EventBusBuilder builder() {
        return new EventBusBuilder();
    }

為什么給我們提供這個方法伟骨,我們不可以直接new一個出來嗎燃异?
原來EventBusBuilder的構(gòu)造函數(shù)不是public的。回俐。。仅颇。但是EventBusBuilder有個方法可以得到EventBus實例

/**
     * Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' values. Must be
     * done only once before the first usage of the default EventBus.
     *
     * @throws EventBusException if there's already a default EventBus instance in place
     */
    public EventBus installDefaultEventBus() {
        synchronized (EventBus.class) {
            if (EventBus.defaultInstance != null) {
                throw new EventBusException("Default instance already exists." +
                        " It may be only set once before it's used the first time to ensure consistent behavior.");
            }
            EventBus.defaultInstance = build();
            return EventBus.defaultInstance;
        }
    }

下面放上創(chuàng)建自己配制EventBusBuilder的EventBus實例完整代碼

EventBus e   = EventBus.builder()
              .eventInheritance(true)
              .ignoreGeneratedIndex(true)
              .installDefaultEventBus();

接著往下走,得到了EventBus的時候后就要register忘瓦。

/**
     * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
     * are no longer interested in receiving events.
     * <p/>
     * Subscribers have event handling methods that must be annotated by {@link Subscribe}.
     * The {@link Subscribe} annotation also allows configuration like {@link
     * ThreadMode} and priority.
     */
    public void register(Object subscriber) {
        //得到訂閱者的運行時類Class對象
        Class<?> subscriberClass = subscriber.getClass();
        //訂閱者中被包裝成SubscriberMethod對象的接收方法的集合
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

源碼的注釋是注冊訂閱者接收時間,不再使用的時候通過unregister(Object)方法取消注冊耕皮。注冊者必須含有一個處理事件并含有Subscribe注解的方法,注釋中允許配置ThreadMode等屬性凌停。
通過subscriberMethodFinder.findSubscriberMethods(subscriberClass)這個方法找到訂閱中接收事件的方法并包裝成SubscriberMethod對象,點進(jìn)去看一下

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        //在緩存中查找是否含有SubscriberMethod集合
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
         //ignoreGeneratedIndex默認(rèn)為為false
        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;
        }
    }

首先通過緩存找到是否SubscriberMethod對象台诗,我們第一次注冊該訂閱者,所有緩存中是沒有的拉队。接著判斷ignoreGeneratedIndex(是否忽略生成的索引),這個可以在EventBusBuildr中去設(shè)置粱快,默認(rèn)為false。所以執(zhí)行 subscriberMethods = findUsingInfo(subscriberClass)

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);
            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);
    }

private FindState prepareFindState() {
        //private static final int POOL_SIZE = 4;
       // private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
        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();
    }

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) {
                SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
                if (info != null) {
                    return info;
                }
            }
        }
        return null;
    }

void initForSubscriber(Class<?> subscriberClass) {
            this.subscriberClass = clazz = subscriberClass;
            skipSuperClasses = false;
            subscriberInfo = null;
        }

這里的findState.subscriberInfo為null皆尔,執(zhí)行findUsingReflectionInSingleClass(findState)

private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            //通過反射獲取訂閱者中的所有方法的所有公用(public)方法包括其繼承類的公用方法,包括它所實現(xiàn)接口的方法慷蠕。
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            //通過反射獲取訂閱者中類所有方法,包括公共流炕、保護(hù)、默認(rèn)(包)訪問和私有方法每辟,但不包括繼承的方法。包括它所實現(xiàn)接口的方法渠欺。
            methods = findState.clazz.getMethods(); 
            //跳過檢查父類
            findState.skipSuperClasses = true;
        }
           //遍歷所有找到的方法
        for (Method method : methods) {
           //拿到方法中修飾符的類型
            int modifiers = method.getModifiers();
           //判斷方法中的修飾符是否為public且沒有ABSTRACT、STATIC挠将、BRIDGE、SYNTHETIC等關(guān)鍵字
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
               //以數(shù)組形式拿到方法中的參數(shù)
                Class<?>[] parameterTypes = method.getParameterTypes();
                //判斷數(shù)組的長度是否為1
                if (parameterTypes.length == 1) {
                   //拿到方法的注解
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                         //eventType是EventBus的Model類
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            //把訂閱者中接收事件的方法包裝成一個SubscriberMethod對象
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
               //參數(shù)長度>1時拋出異常
                   } 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);
                }
               //方法中有不正確的關(guān)鍵字拋出異常
            } 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乳丰。檢查是否已添加在當(dāng)前訂閱者中處理事件的方法
    *    2。檢查接收事件的方法的聲明類
    */
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.
            Object existing = anyMethodByEventType.put(eventType, method);
            if (existing == null) {
                return true;
            } else {
                if (existing instanceof Method) {
                    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);
            }
        }

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)) {
                // Only add if not already found in a sub class
                return true;
            } else {
                // Revert the put, old class is further down the class hierarchy
                subscriberClassByMethodKey.put(methodKey, methodClassOld);
                return false;
            }
        }

這段代碼很長产园,大致就是檢查接收事件的方法是否符合規(guī)范(EventBus通過此規(guī)范找到接收事件的方法)之后添加進(jìn)接收事件方法的集合夜郁,否則拋出異常什燕。并且是通過反射實現(xiàn)的拂酣。接著檢查訂閱者是否還有父類,沒有則跳出循環(huán)


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

跳出循環(huán)后婶熬,拿到接收事件方法的集合,并重置findState對象

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
        List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
        findState.recycle();
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                if (FIND_STATE_POOL[i] == null) {
                    FIND_STATE_POOL[i] = findState;
                    break;
                }
            }
        }
        return subscriberMethods;
    }

接著遍歷SubscriberMethod的集合赵颅,訂閱事件

 for (SubscriberMethod subscriberMethod : subscriberMethods) {
                //subscriber訂閱者。subscriberMethod接收事件的方法
                subscribe(subscriber, subscriberMethod);
            }

 // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
         //EventBus Model類
        Class<?> eventType = subscriberMethod.eventType;
        //包裝成訂閱請求
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        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();
        //訂閱優(yōu)先級
        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);
        //將事件訂閱捂刺,之后post會從typesBySubscriber中獲取訂閱者并發(fā)送事件
        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();
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

以上就是訂閱事件的處理了,遍歷subscriberMethods集合族展,把每個subscriberMethods再次包裝成訂閱請求并添加進(jìn)typesBySubscriber(hashmap)中,之后post或就會從中取出請求并給訂閱者發(fā)送消息仪缸。
歸根到底Event使用觀察者模式+反射完成它的運作

好了贵涵,EventBus事件訂閱部分講完了恰画。下一篇EventBus源碼解析(二)post普通消息發(fā)送

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拴还,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌片林,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拇厢,死亡現(xiàn)場離奇詭異,居然都是意外死亡孝偎,警方通過查閱死者的電腦和手機(jī)凉敲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門衣盾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來爷抓,“玉大人,你說我怎么就攤上這事蓝撇」矗” “怎么了渤昌?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長独柑。 經(jīng)常有香客問我,道長忌栅,這世上最難降的妖魔是什么车酣? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮贫悄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘娘摔。我一直安慰自己,他們只是感情好晰筛,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著读第,像睡著了一般。 火紅的嫁衣襯著肌膚如雪怜瞒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天吴汪,我揣著相機(jī)與錄音,去河邊找鬼漾橙。 笑死,一個胖子當(dāng)著我的面吹牛霜运,可吹牛的內(nèi)容都是我干的脾歇。 我是一名探鬼主播淘捡,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼焦除!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起膘魄,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瓣距,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蹈丸,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡呐芥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年奋岁,在試婚紗的時候發(fā)現(xiàn)自己被綠了思瘟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闻伶。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡滨攻,死狀恐怖蓝翰,靈堂內(nèi)的尸體忽然破棺而出光绕,到底是詐尸還是另有隱情畜份,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布爆雹,位于F島的核電站,受9級特大地震影響钙态,放射性物質(zhì)發(fā)生泄漏慧起。R本人自食惡果不足惜册倒,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望剩失。 院中可真熱鬧,春花似錦拴孤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽芒粹。三九已至,卻和暖如春大溜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钦奋。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工疙赠, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人圃阳。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像捍岳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子睬隶,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

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

  • EventBus源碼分析(一) EventBus官方介紹為一個為Android系統(tǒng)優(yōu)化的事件訂閱總線,它不僅可以很...
    蕉下孤客閱讀 4,007評論 4 42
  • 前言 在上一篇文章:EventBus 3.0初探: 入門使用及其使用 完全解析中银萍,筆者為大家介紹了EventBus...
    丶藍(lán)天白云夢閱讀 15,843評論 21 128
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理窖贤,服務(wù)發(fā)現(xiàn)砖顷,斷路器赃梧,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,104評論 25 707
  • 昨天晚飯后滤蝠,天公仍不作美授嘀,雨沒有絲毫停歇的意思物咳。 我習(xí)慣性倡議H和我一起去散步蹄皱,H說:下著雨呢览闰。我說:下雨就不能去...
    千禾隨筆閱讀 720評論 4 23