Eventbus3代碼分析(六):SubscriberMethodFinder類


SubscriberMethodFinder類

SubscriberMethodFinder
大體就是去注冊后對應的方法

其中宝惰,屬性

private static final int BRIDGE = 0x40;

有對應的說明:

In newer class files, compilers may add methods. Those are called bridge or synthetic methods.
EventBus must ignore both. There modifiers are not public but defined in the Java class file format:
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6-200-A.1

自己也沒有研究過jvm,簡單貼一下圖

下面對應的變量修飾符

private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;

也就是粱腻,
方法上的注解庇配,會忽略這幾種修飾符的方法


屬性方面

先重點看一下這2個容器


一個是 : 本類存儲的容器類,用于 查找 和 存儲
另一個是 :傳入的引用


方法大體了解

Eventbus3

這里绍些,我們可以發(fā)現捞慌,
除了 構造 和 findSubscriberMethods方法 是 public對外的
其他,都是 private 的
也就是柬批,我們主要認識 findSubscriberMethods方法 即可

和EventBus 2.4 對比


EventBus 2.4

我們大體可以猜測啸澡,其他多余出來的方法,都是用于判斷反射的


findSubscriberMethods方法

先一起大體過一下

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    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;
    }
}

這里氮帐,大體就是

  • 如果有Map緩存中有值嗅虏,就直接獲取
  • 如果 ignoreGeneratedIndex為true 就 findUsingReflection(subscriberClass) 通過反射獲取
  • 如果 ignoreGeneratedIndex為false ,就 findUsingInfo(subscriberClass) 獲取
  • 最后
    • subscriberMethods 為空上沐,內部報異常(對外是不會提示的)
    • subscriberMethods 不空皮服,把對應的key為 subscriberClass, value為subscriberMethods 加入到 Map緩存中

findUsingInfo方法

上面提到的
如果Map緩存中 沒有值,并且 ignoreGeneratedIndex為false的時候
會調用這個方法
具體看下源碼

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

這里通過 prepareFindState()冰更, 獲取 FindState 對象
(具體方法产徊,見下面的方法說明)


FindState 靜態(tài)內部類

這里因為上面的方法會獲取這個對象
所以,我們先來看一下這個對象

static class FindState {
    final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
    final Map<Class, Object> anyMethodByEventType = new HashMap<>();
    final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
    final StringBuilder methodKeyBuilder = new StringBuilder(128);

    Class<?> subscriberClass;
    Class<?> clazz;
    boolean skipSuperClasses;
    SubscriberInfo subscriberInfo;

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

    void recycle() {
        subscriberMethods.clear();
        anyMethodByEventType.clear();
        subscriberClassByMethodKey.clear();
        methodKeyBuilder.setLength(0);
        subscriberClass = null;
        clazz = null;
        skipSuperClasses = false;
        subscriberInfo = null;
    }

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

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

這個代碼比較長蜀细,我們大體看一下即可


  • initForSubscriber方法
    • 初始化靜態(tài)內部類
    • 傳遞 Class<?> 對象
    • 設置 SubscriberInfo 為空
  • recycle方法
    • 所有集合都 clear 舟铜, 引用對象都設置為null
    • 因為是靜態(tài)的, 所以 回收后再次調用
  • checkAdd方法
    • boolean類型
    • 將對應的 key 為 eventType奠衔, value 為 Method 放入到 Map中
    • 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.
    • 這里有2次check谆刨, 第一次check是比較快, 第二次會再次check归斤,通常一個subscriber不會監(jiān)聽2個相同的event
  • checkAddWithMethodSignature方法
    • 也就是第二次check痊夭,通過拼接一個string來作為一個key
  • moveToSuperclass方法
    • 如果 skipSuperClasses為true,或者 Superclass的startsWith對應的時候脏里,設置 clazz = null
    • 其他時候她我,返回 clazz = clazz.getSuperclass().getName()

大體就是

  • 初始化對應的類屬性
  • 再就是check是否已經add過了(這里是2次check)
  • 把class的屬性,設置為Super的class(特殊情況設置為null)

prepareFindState方法

這里是前面 findUsingInfo方法 中用到的
也就是用來獲取FindState對象的方法

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

這里迫横,知道這里有一個 FIND_STATE_POOL 對象池
對象個數 POOL_SIZE 為 4
具體對象為 FindState
是一個 靜態(tài)內部類
for循環(huán)取值:

  • 如果不為空番舆, 則找到對象池中 一個不為空的對象
  • 如果為空,就返回一個新的 FindState對象()

getMethodsAndRelease 方法

先看一下源碼

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

這里大體就是獲取 FindState中的List<SubscriberMethod>類型的對象subscriberMethods
將對應的FindState對象放入對象池中
最后返回List<SubscriberMethod>的值


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

也就是如果傳入的findState對象不空矾踱,并且有SubscriberInfo的值
(這塊自己感覺沒有賦值的地方恨狈,應該為null,具體先不糾結)
或者對應的值呛讲,并且SubscriberInfo不為空禾怠,Class相同的時候,
返回對應的SubscriberInfo對象

如果上面不符合贝搁,會取從構造傳進來的subscriberInfoIndexes對象中
for循環(huán)吗氏,獲取第一個 SubscriberInfo 對象


findUsingReflection方法

這里就不貼代碼了
大體為:

  • 先通過 prepareFindState方法,獲得內部靜態(tài)類的FindState對象
  • 再while (findState.clazz != null)循環(huán)
    • 通過findUsingReflectionInSingleClass方法
    • 每次獲取一個類后雷逆,通過 FindState的moveToSuperclass()方法牲证,設置class引用
      • 如果有父類,設置為class=class.getSuperclass()
      • 如果沒有父類关面,賦值class=null坦袍,從而讓findUsingReflection中,離開while循環(huán)

findUsingReflectionInSingleClass方法

上面用到的
大體為:

  • 獲取每個SingleClass的方法等太,
  • 在除去其他修飾的方法后捂齐,獲取 注解的 Subscribe對象,
  • 放入FindState對象的subscriberMethods容器中

簡單總結

對外缩抡,就2個方法

  • 一個是構造奠宜,傳入 List<SubscriberInfoIndex> subscriberInfoIndexes 等變量
  • 一個是 findSubscriberMethods, 去找對應的注冊者Subscriber的方法

對應的方法,會存入
Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>()
容器中

  • 如果容器中有压真,會從容器中獲取 List<SubscriberMethod>
  • 如果容器中沒有
    • ignoreGeneratedIndex 為 true娩嚼,則通過反射去拿具體的類中獲取
    • ignoreGeneratedIndex 為 false,則 通過 findUsingInfo方法獲取
      • FindState對象池中滴肿,獲取對象
      • 通過while循環(huán)(這里后面的方法會設置對象岳悟,確定可以跳出循環(huán))
      • 遍歷,獲取 List<SubscriberMethod> 對象
        • 如果FindState 對象池 中有泼差,則直接獲取
        • 如果 FindState 對象池中沒有贵少,則 從構造傳入的subscriberInfoIndexes對象中獲取
  • 其實,最終也就獲取 List<SubscriberMethod>
    • 通過傳進來的Class<?> 對象
    • 也就是在 Activity或者Fragment中注冊的Class
    • 獲取@Subscriber 進行注解的方法 們

(其實堆缘,自己不太理解滔灶,這里為什么用一個容器存儲,為什么要這樣寫結構 等等吼肥,只是簡單的敘述了下流程录平,等有時間,再考慮一下)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末缀皱,一起剝皮案震驚了整個濱河市斗这,隨后出現的幾起案子,更是在濱河造成了極大的恐慌唆鸡,老刑警劉巖涝影,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枣察,死亡現場離奇詭異争占,居然都是意外死亡,警方通過查閱死者的電腦和手機序目,發(fā)現死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門臂痕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人猿涨,你說我怎么就攤上這事握童。” “怎么了叛赚?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵澡绩,是天一觀的道長。 經常有香客問我俺附,道長肥卡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任事镣,我火速辦了婚禮步鉴,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己氛琢,他們只是感情好喊递,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著阳似,像睡著了一般骚勘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上障般,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天调鲸,我揣著相機與錄音,去河邊找鬼挽荡。 笑死藐石,一個胖子當著我的面吹牛,可吹牛的內容都是我干的定拟。 我是一名探鬼主播于微,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼青自!你這毒婦竟也來了株依?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤延窜,失蹤者是張志新(化名)和其女友劉穎恋腕,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體逆瑞,經...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡荠藤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了获高。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哈肖。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖念秧,靈堂內的尸體忽然破棺而出淤井,到底是詐尸還是另有隱情,我是刑警寧澤摊趾,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布币狠,位于F島的核電站,受9級特大地震影響砾层,放射性物質發(fā)生泄漏漩绵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一梢为、第九天 我趴在偏房一處隱蔽的房頂上張望渐行。 院中可真熱鬧轰坊,春花似錦、人聲如沸祟印。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蕴忆。三九已至颤芬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間套鹅,已是汗流浹背站蝠。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留卓鹿,地道東北人菱魔。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像吟孙,于是被迫代替她去往敵國和親澜倦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理杰妓,服務發(fā)現藻治,斷路器,智...
    卡卡羅2017閱讀 134,665評論 18 139
  • 轉至元數據結尾創(chuàng)建: 董瀟偉巷挥,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,721評論 0 9
  • 一:java概述:1桩卵,JDK:Java Development Kit,java的開發(fā)和運行環(huán)境倍宾,java的開發(fā)工...
    ZaneInTheSun閱讀 2,654評論 0 11
  • 喝酒雏节,沒喝多,低調低調凿宾,一切安好矾屯,晚安兼蕊!
    何時再出發(fā)閱讀 95評論 1 0
  • 相遇在海面 你遨游九天 我沉于深淵
    花敗不開閱讀 142評論 0 0