EventBus源碼分析二

2.2Subscriber向Eventbus注冊(cè)自己

2.2.1 register方法

接下來(lái)看register方法代碼:

    public void register(Object subscriber) {
        //獲得訂閱者的class對(duì)象
        Class<?> subscriberClass = subscriber.getClass();
        //根據(jù)訂閱者對(duì)象的Class對(duì)象查找當(dāng)前訂閱者的訂閱方法(所有事件響應(yīng)函數(shù))
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                //循環(huán)每個(gè)事件響應(yīng)函數(shù)
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

subscriberClass就是訂閱者所屬的Class,如MainActivity.class,之后利用subscriberMethodFinder查找subscriberClass中的所有事件響應(yīng)函數(shù)刊橘,先了解下SubscriberMethod類:

public class SubscriberMethod {
    final Method method;//方法
    final ThreadMode threadMode;//執(zhí)行線程
    final Class<?> eventType;//接收的事件類型
    final int priority;//優(yōu)先級(jí)
    final boolean sticky;
....
}

SubscriberMethod類中封裝了某個(gè)事件響應(yīng)函數(shù)的信息撤蚊,包括:Method對(duì)象敬鬓、執(zhí)行環(huán)境驮吱、接收的事件類型、優(yōu)先級(jí)和是否是sticky事件署拟。

2.2.2 SubscriberMethodFinder的實(shí)現(xiàn)

SubscriberMethodFinder類用來(lái)查找和緩存訂閱者中的訂閱方法(事件響應(yīng)函數(shù))的信息類铁瞒。

SubscriberMethodFinder .findSubscriberMethods()方法如下:

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        //先從METHOD_CACHE查看是否有緩存,key:訂閱類的class對(duì)象,value:保存訂閱類中所有訂閱方法(事件響應(yīng)函數(shù))。
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }

        //是否忽略注解器生成的索引類(MyEventBusIndex)
        if (ignoreGeneratedIndex) {
            //在運(yùn)行時(shí)利用反射來(lái)獲得訂閱類中的所有訂閱方法(事件響應(yīng)函數(shù))
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            //在編譯時(shí)從注解器生成的索引類(MyEventBusIndex)中獲得所有訂閱方法(事件響應(yīng)函數(shù))
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
            //把訂閱類中所有訂閱方法(事件響應(yīng)函數(shù))緩存到METHOD_CACHE
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

該方法首先從緩存中獲取訂閱類的訂閱方法(事件響應(yīng)函數(shù))信息新娜,如果沒有則通過(guò)以下兩種方式來(lái)獲日栽:
1、在編譯時(shí)杯活,通過(guò)EventBusAnnotationProcessor(注解處理器)結(jié)合@Subscriber所注解方法生成的MyEventBusIndex類中獲取匆帚。

有關(guān)于這種方式請(qǐng)到這:EventBus3.0新特性之Subscriber Index

2、在運(yùn)行時(shí)旁钧,通過(guò)反射來(lái)獲取訂閱類中訂閱方法(事件響應(yīng)函數(shù))的信息

2.2.2 使用反射獲取訂閱信息

    private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
        SubscriberMethodFinder.FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            //真正的通過(guò)反射來(lái)獲得訂閱方法信息邏輯
            findUsingReflectionInSingleClass(findState);
            //查找父類的訂閱方法
            findState.moveToSuperclass();
        }
        // 獲取findState中的subscriberMethods(也就是訂閱方法List)并返回
        return getMethodsAndRelease(findState);

    }

FindState其實(shí)就是一個(gè)封裝了訂閱相關(guān)信息的類吸重,最終是通過(guò)findUsingReflectionInSingleClass()來(lái)具體獲得相關(guān)訂閱方法的信息的:

    private void findUsingReflectionInSingleClass(SubscriberMethodFinder.FindState findState) {
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            //這種方式獲取Method類型數(shù)組比getMethods方法更快,特別是當(dāng)在訂閱者是胖類比如像activity時(shí)歪今。
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            //如果getDeclaredMethods方式獲取Method類型數(shù)組時(shí)嚎幸,拋出異常則改為使用getMethods方式獲取到訂閱類中的所有方法
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            //選擇是public和非static,非abstract,非bridge,非synthetic的方法
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                //保證訂閱方法只有一個(gè)方法參數(shù)
                if (parameterTypes.length == 1) {
                    //判斷此方法對(duì)象是否有被Subscribe注解
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        Class<?> eventType = parameterTypes[0];
                        //校驗(yàn)是否添加該訂閱方法
                        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("@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");
            }
        }
    }

此方法執(zhí)行完后,我們訂閱類的所有訂閱方法都已經(jīng)被保存在FindState對(duì)象寄猩,最后再通過(guò)getMethodsAndRelease()解析得到List<SubscriberMethod>嫉晶。
至此,通過(guò)反射獲取訂閱方法信息這種方式已經(jīng)分析完了。

2.2.3 使用EventBusAnnotationProcessor獲取訂閱信息

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        SubscriberMethodFinder.FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        //不只是獲得當(dāng)前訂閱信息替废,還要獲得其到頂層父類的所有訂閱信息
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) {
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    //檢查是否添加過(guò)此訂閱方法
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        //添加訂閱方法
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                //如果發(fā)現(xiàn)獲取不到subscriberInfo的話,就還是要使用反射來(lái)獲取箍铭。
                findUsingReflectionInSingleClass(findState);
            }
            //會(huì)切換findState.clazz對(duì)象為父類的Class對(duì)象
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

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

subscriberInfoIndexes是屬于SubscriberInfoIndex類型數(shù)組。SubscriberInfoIndex是一個(gè)接口椎镣,MyEventBusIndex實(shí)現(xiàn)了這個(gè)接口诈火。
subscriberInfoIndexes是在執(zhí)行addIndex方法被初始化并把MyEventBusIndex對(duì)象添加到此集合中。

EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex())状答;

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

MyEventBusIndex起作用是在SubscriberMethodFinder的getSubscriberInfo()中冷守,以findState.clazz(訂閱者的class對(duì)象)為key,在MyEventBusIndex.SUBSCRIBER_INDEX中查找惊科,如果查找到了則直接返回拍摇。
至此,通過(guò)索引類獲取訂閱方法信息這種方式已經(jīng)分析完了馆截。

2.2.3 subscribe

回到2.2.1 register方法充活,在獲取subscriberMethods之后,就是遍歷各訂閱方法孙咪,并執(zhí)行subscribe方法堪唐。

   private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
       //獲取訂閱方法(事件響應(yīng)函數(shù))的事件類型
       Class<?> eventType = subscriberMethod.eventType;
       Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
       //從subscriptionsByEventType里檢查是否已經(jīng)添加過(guò)該Subscription,如果添加過(guò)就拋出異常
       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();
       for (int i = 0; i <= size; i++) {
           //根據(jù)優(yōu)先級(jí)priority將當(dāng)前訂閱者信息插入到訂閱者隊(duì)列中
           if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
               subscriptions.add(i, newSubscription);
               break;
           }
       }

       //獲取訂閱者所有訂閱的事件類型
       List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
       if (subscribedEvents == null) {
           subscribedEvents = new ArrayList<>();
           //把訂閱者對(duì)象作為key,對(duì)應(yīng)訂閱的事件類型集合作為value翎蹈,保存到typesBySubscriber中淮菠。
           typesBySubscriber.put(subscriber, subscribedEvents);
       }
       //將該事件類型添加到typesBySubscriber中
       subscribedEvents.add(eventType);

       //如果接收sticky事件,立即分發(fā)sticky事件
       if (subscriberMethod.sticky) {
           //eventInheritance 表示是否分發(fā)訂閱了事件的父類的事件響應(yīng)函數(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();
                   if (eventType.isAssignableFrom(candidateEventType)) {
                       Object stickyEvent = entry.getValue();
                       checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                   }
               }
           } else {
               Object stickyEvent = stickyEvents.get(eventType);
               checkPostStickyEventToSubscription(newSubscription, stickyEvent);
           }
       }
   }

在此方法中,主要完成以下事情:

  1. 事件與訂閱類中訂閱方法進(jìn)行綁定荤堪。
  2. 訂閱對(duì)象與所有訂閱的事件類型進(jìn)行綁定
  3. 對(duì)sticky事件進(jìn)行相關(guān)處理

至此合陵,完成對(duì)[2.2Subscriber向Eventbus注冊(cè)自己]過(guò)程的源碼分析。

Subscriber向Eventbus注冊(cè)自己的執(zhí)行流程圖:

register方法執(zhí)行流程圖
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末澄阳,一起剝皮案震驚了整個(gè)濱河市拥知,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碎赢,老刑警劉巖低剔,帶你破解...
    沈念sama閱讀 211,423評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異肮塞,居然都是意外死亡襟齿,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門枕赵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)猜欺,“玉大人,你說(shuō)我怎么就攤上這事拷窜】螅” “怎么了涧黄?”我有些...
    開封第一講書人閱讀 157,019評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)赋荆。 經(jīng)常有香客問我笋妥,道長(zhǎng),這世上最難降的妖魔是什么糠睡? 我笑而不...
    開封第一講書人閱讀 56,443評(píng)論 1 283
  • 正文 為了忘掉前任挽鞠,我火速辦了婚禮,結(jié)果婚禮上狈孔,老公的妹妹穿的比我還像新娘。我一直安慰自己材义,他們只是感情好均抽,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,535評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著其掂,像睡著了一般油挥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上款熬,一...
    開封第一講書人閱讀 49,798評(píng)論 1 290
  • 那天深寥,我揣著相機(jī)與錄音,去河邊找鬼贤牛。 笑死惋鹅,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的殉簸。 我是一名探鬼主播闰集,決...
    沈念sama閱讀 38,941評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼般卑!你這毒婦竟也來(lái)了武鲁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,704評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蝠检,失蹤者是張志新(化名)和其女友劉穎沐鼠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叹谁,經(jīng)...
    沈念sama閱讀 44,152評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡饲梭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,494評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了本慕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片排拷。...
    茶點(diǎn)故事閱讀 38,629評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖锅尘,靈堂內(nèi)的尸體忽然破棺而出监氢,到底是詐尸還是另有隱情布蔗,我是刑警寧澤,帶...
    沈念sama閱讀 34,295評(píng)論 4 329
  • 正文 年R本政府宣布浪腐,位于F島的核電站纵揍,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏议街。R本人自食惡果不足惜泽谨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,901評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望特漩。 院中可真熱鬧吧雹,春花似錦、人聲如沸涂身。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蛤售。三九已至丁鹉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悴能,已是汗流浹背揣钦。 一陣腳步聲響...
    開封第一講書人閱讀 31,978評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留漠酿,地道東北人冯凹。 一個(gè)月前我還...
    沈念sama閱讀 46,333評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像记靡,于是被迫代替她去往敵國(guó)和親谈竿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,499評(píng)論 2 348

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

  • EventBus源碼分析(二) 在之前的一篇文章EventBus源碼分析(一)分析了EventBus關(guān)于注冊(cè)注銷以...
    蕉下孤客閱讀 1,651評(píng)論 0 10
  • 我每周會(huì)寫一篇源代碼分析的文章,以后也可能會(huì)有其他主題.如果你喜歡我寫的文章的話,歡迎關(guān)注我的新浪微博@達(dá)達(dá)達(dá)達(dá)s...
    SkyKai閱讀 24,913評(píng)論 23 184
  • 前面已經(jīng)說(shuō)了EventBus的基本用法摸吠,下面我們一步步的看下Eventbus中到底做了些什么空凸,為什么使用Index...
    fishpan閱讀 304評(píng)論 0 0
  • 前言# 本來(lái)想把EventBus的使用和編譯庫(kù)的分析一起說(shuō),但是覺得篇幅有點(diǎn)太大了寸痢,編譯庫(kù)的東西雖然不多也不復(fù)雜呀洲,...
    珠穆朗瑪小王子閱讀 512評(píng)論 0 0
  • EventBus用法及源碼解析目錄介紹1.EventBus簡(jiǎn)介1.1 EventBus的三要素1.2 EventB...
    楊充211閱讀 1,888評(píng)論 0 4