EventBus 源碼的閱讀02 - EventBus 的註冊(上篇)

上一章分析學習了 EventBus 對象的創建, 本章內容將對 EventBus 的註冊源碼進行分析.
由於代碼較長, 所以分爲上下兩個章節.

  • 上篇: 分析的是如何獲取訂閱者類中的訂閱方法的.
  • 下篇: 分析的是在獲取到訂閱者類中的所有訂閱方法後如何進行訂閱的流程.

還記得嗎, 在上一章 EventBus 對象創建的時候, 初始化了那些 ListMap, 其中有兩個較爲重要的, 分別是:

  • private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType
    事件到訂閱者列表的 map, 發佈事件的時候可以根據事件獲取訂閱者列表, 這樣可以逐一反射調用訂閱者的事件處理方法.
     
  • private final Map<Object, List<Class<?>>> typesBySubscriber
    訂閱者到事件列表的 map, 這樣訂閱者向 EventBus 解除註冊 (unregister) 的時候可以根據訂閱者獲取該訂閱者訂閱的所有事件, 對每個事件分別解除註冊.

簡單來說本章的核心思想就是: 訂閱者調用 EventBus.getDefault().register(this) 進行註冊. EventBus 就會通過反射尋找訂閱者內所有的訂閱方法, 然後進行挨個遍歷進行訂閱. 同時會更新上面的兩個 Map. 如果是粘性 sticky 事件, 在訂閱的時候就取出保存的 sticky 事件直接發送, 這樣就做到了發佈者先發布事件, 之後訂閱者訂閱事件, 接收訂閱之前發佈的粘性事件.
Ps: 上篇只分析是如何獲取訂閱者內所有的訂閱方法.


本章涉及到的類及方法

|---EventBus.register()
|       1---SubscriberMethodFinder.findSubscriberMethods()
|             1.1---SubscriberMethodFinder.findUsingInfo()
|                   1.1.1---SubscriberMethodFinder.prepareFindState()
|                   1.1.2---FindState.initForSubscriber()
|                   1.1.3---SubscriberMethodFinder.getSubscriberInfo()
|                   1.1.4---SubscriberMethodFinder.findUsingReflectionInSingleClass()
|                   1.1.5---FindState.moveToSuperclass()
|                   1.1.6---SubscriberMethodFinder.getMethodsAndRelease()
|       2---EventBus.subscribe() 在下篇進行分析


EventBus 3.0 後的版本中增加了註解處理器, 在程序的編譯時候, 就可以根據註解生成相對應的代碼, 相對於之前的直接通過運行時反射, 大大的提高了程序的運行效率. 目前還是先使用最基本的運行時反射的方式來分析, 後面也會根據運行時反射的方式來分析.


EventBus.register()

public void register(Object subscriber) {
    //通過反射獲得訂閱者的 Class 對象
    Class<?> subscriberClass = subscriber.getClass();
    //通過訂閱者 Class 對象找到該對象內所有的訂閱方法集合
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        //遍歷並進行單個方法的訂閱
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

register 方法中, 通過反射獲得我們傳入的 thisclass 對象, 也就是訂閱者的 class 對象. 然後調用了 subscriberMethodFinder.findSubscriberMethods 傳入訂閱者 class 對象後獲得訂閱對象內的所有訂閱方法的集合. 這個是獲得訂閱方法集合的入口. 接着遍歷所有的訂閱方法挨個對其進行訂閱..


1. SubscriberMethodFinder.findSubscriberMethods()

//是一個方法緩存池. key 爲我們訂閱者的 class 對象. value 爲訂閱者內所有的訂閱方法
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
      //分析 1
      List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
      if (subscriberMethods != null) {
          return subscriberMethods;
      }
      //分析 2
      if (ignoreGeneratedIndex) {
          //通過反射獲取 subscriberMethods
          subscriberMethods = findUsingReflection(subscriberClass); 
      } else {
          //通過註解器生成的 MyEventBusIndex 索引類信息獲取 subscriberMethods.
          //如果沒有配置 MyEventBusIndex索引類, 依然通過通過反射獲取 subscriberMethods
          subscriberMethods = findUsingInfo(subscriberClass);
      }
      if (subscriberMethods.isEmpty()) {
          throw new EventBusException("Subscriber " + subscriberClass
                  + " and its super classes have no public methods with the @Subscribe annotation");
      } else {
           //分析 3
          METHOD_CACHE.put(subscriberClass, subscriberMethods);
          return subscriberMethods;
      }
  }
  • 分析 1
    從方法緩存池中獲取我們傳入訂閱者的class對象的所有訂閱方法集合. 若存在就直接返回.
    首先看到根據我們傳入的訂閱者的class對象從 METHOD_CACHE 這個map中獲取一個 SubscriberMethod類型的列表.

    那麼METHOD_CACHE是用來存放什麼呢? 看一下它的聲明.
    private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>()
    METHOD_CACHE 是一個方法緩存池, 以訂閱者的class對象爲key, 以訂閱者內部所有的訂閱方法集合爲value

    爲什麼說METHOD_CACHEvalue SubscriberMethod是訂閱者內部的所有訂閱方法的集合呢?

    SubscriberMethod 就是將我們平時使用 EventBus 過程中的那些參數基本上都封裝起來了, 例如 Method 方法. threadMode線程模型, eventType事件類型, priority優先級, sticky是否是粘性事件等等.

  • 分析 2
    根具ignoreGeneratedIndex的值來採用不同的方式獲取訂閱方法的集合
    ignoreGeneratedIndex: 是否忽略註解器生成的 MyEventBusIndex索引類, 默認爲 false. 可以通過EventBusBuilder來設置它的值.

    由於我們一般都是使用通過EventBus單例模式來獲取默認的EventBus對象 也就是第一章是分析的那種方式來創建. 所以就針對ignoreGeneratedIndexfalse的情況下看一下EventBus是如何獲得訂閱方法集合的. 後面也會分析有索引類的情況下的流程.

    Ps: MyEventBusIndex索引類在項目重新rebuild以後, 會自動生成在build文件夾下, 類名也可以自己定義. 如何生成 MyEventBusIndex 索引類以及他的使用,可以參考官方文檔

  • 分析 3
    subscriberClass 就是訂閱者的class對象.
    subscriberMethods 就是 findUsingInfo(subscriberClass) 返回的當前訂閱者的所有訂閱方法集合.
    將當前訂閱者class對象的所有訂閱方法存儲到METHOD_CACHE 中並返回 subscriberMethods

我們本章主要分析的是調用findUsingInfo(subscriberClass)這個方法, 看它是如何拿到當前訂閱者 class 對象內的所有訂閱方法的.


1.1.SubscriberMethodFinder.findUsingInfo()

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
      //從 findState 池中獲取 findState 對象, 池中沒有就創建一個新的對象
      FindState findState = prepareFindState();
      findState.initForSubscriber(subscriberClass);
      ...
  }

由於在這個方法內調用的方法較多, 所以會逐個分析.
先是通過調用 prepareFindState() 方法, 獲得了一個 FindState 對象. 接着調用了 FindState.initForSubscriber 方法. 看名字大概能猜得出, 是做了一些初始化的操作.
那我們先來看 prepareFindState() 方法.


1.1.1.SubscriberMethodFinder.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) {
        //長度爲 4, 遍歷 findState 對象池
        for (int i = 0; i < POOL_SIZE; i++) {
            FindState state = FIND_STATE_POOL[i];
            //如果遍歷到的對象非空,表示找到了可複用的 findState
            if (state != null) {
                //將該位置清空, 爲了以後再來複用
                FIND_STATE_POOL[i] = null;
                //返回這個 findState
                return state;
            }
        }
    }
    //如果遍歷完都沒有找到可用的 findState, 就創建一個.
    return new FindState();
}

首先是初始化了一個長度爲 4 的 FindState類型數組 FIND_STATE_POOL, 也可以叫它 FindState 對象池. 這個方法就是從 FindState 對象池中獲取一個 FindState 對象返回, 如果對象池中沒有找到可複用的那麼就創建一個新的對象返回.

從 FIND_STATE_POOL 中取出不爲空的FindState對象, 然後將對應位置置空, 返回FindState對象, 這樣可以避免多線程讀寫衝突以及讀寫不一致情況發生.

在 1.1 中獲取了 FindState 對象後, 又調用了它的 initForSubscriber()初始化方法. 現在跟進進入 FindState 看一下這是什麼類.


1.1.2.FindState.initForSubscriber

static class FindState {
      //用來保存所有的訂閱方法信息
      final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
      //以 Event 事件類型爲 key, 訂閱方法爲 value
      final Map<Class, Object> anyMethodByEventType = new HashMap<>();
      //key 爲方法名, Value 爲訂閱者 class 對象
      final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
      final StringBuilder methodKeyBuilder = new StringBuilder(128);

      //subscriberClass 代表的是訂閱者本身,也就是register函數的參數,自始至終不會變
      // 會在遍歷方法過程中變化, 比如目前在遍歷父類方法,clazz 變量就指向父類
      Class<?> subscriberClass;
      Class<?> clazz;
      //是否跳過父類. 即對父類也要查找, 默認爲 false
      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;
      }
     //判斷訂閱方法是否可以添加到 anyMethodByEventType 中
      boolean checkAdd(Method method, Class<?> eventType) {
         //通常,訂閱者沒有監聽相同事件類型的方法。但是會有一種情況就是 子類訂閱了當前事件, 它的父類也訂閱了
         //如果 key 沒有重複,put 成功,則返回 null, 如果 key 重複了,返回的是被覆蓋前的 valu 值.
          Object existing = anyMethodByEventType.put(eventType, method);
          if (existing == null) {
              return true;
          } else {
              if (existing instanceof Method) {
                  ...
                  anyMethodByEventType.put(eventType, this);
              }
                //再根據方法簽名進行檢查
              return checkAddWithMethodSignature(method, eventType);
          }
      }

      //主要目的還是爲了, 不要出現一個訂閱者有多個相同訂閱方法訂閱同一個事件. 如果有, 就將以前的 value 重新放進去覆蓋掉新的.
      private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
          ...
          //獲得當前 Method 對象表示的方法的類的 Class 對象
          Class<?> methodClass = method.getDeclaringClass();
          //這裏也會返回 null, 或者是覆蓋前的 value 值
          Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
          // 爲 null 表示添加成功並且不是覆蓋.
          // 父類Class.isAssignableFrom(子類Class) 判斷新添加的類是否是被覆蓋類的子類.
          if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
              return true;
          } else {
              //如果不滿足上述判斷, 將被覆蓋前的value, 重新放進去, 並返回 false.
              subscriberClassByMethodKey.put(methodKey, methodClassOld);
              return false;
          }
      }

      //將成員變量clazz指向clazz的父類並排除系統類
      void moveToSuperclass() {
          if (skipSuperClasses) {
              clazz = null;
          } else {
              clazz = clazz.getSuperclass();
              String clazzName = clazz.getName();
              if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") ||clazzName.startsWith("android.") || clazzName.startsWith("androidx.")) {
                  //排除系統類
                  clazz = null;
              }
          }
      }
  }

FindState 類. 它是 SubscriberMethodFinder 中的一個靜態內部類. 內部存儲了對訂閱者class對象一些列的集合, 並保存了一些訂閱者的方法以及對訂閱方法的校驗.

FindState 中又出現了一個陌生的 SubscriberInfo, 這是一個接口, 描述了一個訂閱者應當具備的特性. 具體的實現是在 SimpleSubscriberInfo中, 而SimpleSubscriberInfo的創建是在配置自動生成的索引類MyEventBusIndex中. 後面會對配置索引類的流程也進行分析. 這裏先不管這些.

public interface SubscriberInfo {
    Class<?> getSubscriberClass();//獲取父類列表
    SubscriberMethod[] getSubscriberMethods();//獲取註冊的訂閱方法
    SubscriberInfo getSuperSubscriberInfo();//獲取父類(直接父類)的訂閱信息
    boolean shouldCheckSuperclass();//是否需要對父類進行檢查
}

繼續回到 1.1 findUsingInfo() 中向下分析

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
      //從 findState 池中獲取 findState 對象, 池中沒有就創建一個新的對象
      FindState findState = prepareFindState();
      //傳入訂閱者的 Class 對象, 爲 findState 做一些初始化工作
      findState.initForSubscriber(subscriberClass);
      //循環直到 findState 的 clazz 爲空( clazz 爲 java. 或 android. 或 javax. 等系統類則結束查找). 
      while (findState.clazz != null) {
          //如果沒有配置 MyEventBusIndex 索引類的情況, 這裏默認基本都是返回 null
          findState.subscriberInfo = getSubscriberInfo(findState);
           ... 
      }
      ...
  }

看到初始化完 FindState後, 調用了 getSubscriberInfo()方法將返回值賦值給 findState.subscriberInfo. 不過在沒有配置使用索引類的情況下, 這裏返回的基本都是 null. 跟進 getSubscriberInfo() 方法.


1.1.3.SubscriberMethodFinder.getSubscriberInfo(findState)

private SubscriberInfo getSubscriberInfo(FindState findState) {
    //分析 1
    if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
        SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
        if (findState.clazz == superclassInfo.getSubscriberClass()) {
            return superclassInfo;
        }
    }
    //分析 2
    if (subscriberInfoIndexes != null) {
        for (SubscriberInfoIndex index : subscriberInfoIndexes) {
            SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
            if (info != null) {
                return info;
            }
        }
    }
    return null;
}
  • 分析 1
    FindState.initForSubscriber()的方法中, 就已經將 subscriberInfo 置爲了 null. 所以第一個 if 不會進入.
  • 分析 2
    在沒有配置索引類 MyEventBusIndex的情況下, subscriberInfoIndexes 是在構造方法傳入的, 默認爲 null, 所以這裏直接返回了 null.
    如果配置了索引類 MyEventBusIndex, 並且在我們開始查找訂閱方法的時候並沒有忽略註解器爲我們生成的索引類MyEventBusIndex, (也就是設置ignoreGeneratedIndex值爲true), 就會進入到分析 2 的if內.

這裏只考慮沒有使用索引類的情況. 所以直接返回 null, 繼續回到 1.1findUsingInfo ()

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
      //從 findState 池中獲取 findState 對象, 池中沒有就創建一個新的對象
      FindState findState = prepareFindState();
      //傳入訂閱者的 Class 對象, 爲 findState 做一些初始化工作
      findState.initForSubscriber(subscriberClass);
      //循環直到 findState 的 clazz 爲空( clazz 爲 java. 或 android. 或 javax. 等系統類則結束查找). 
      while (findState.clazz != null) {
          //如果沒有配置 MyEventBusIndex 索引類的情況, 這裏默認基本都是返回 null
          findState.subscriberInfo = getSubscriberInfo(findState);
          if (findState.subscriberInfo != null) {
                ...
          } else {
              //在運行時利用反射來獲取所有被 @Subscribe 註解標註的訂閱方法
              findUsingReflectionInSingleClass(findState);
          }
      }
    ...
  }

接着就進入到 else 中的 findUsingReflectionInSingleClass方法, 開始利用反射來獲取所有添加了@Subscribe註解的的訂閱方法.接着跟進去.


1.1.4.SubscriberMethodFinder.findUsingReflectionInSingleClass(findState)

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        //通過反射獲取訂閱者 class 對象內的所有方法.
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        ...
        ...
    }
    //對獲取到的所有方法進行遍歷
    for (Method method : methods) {
        //獲取到方法的修飾符
        int modifiers = method.getModifiers();
        //判斷修飾符是否是 public, 並且是否可以忽略
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            //獲取方法的參數, 後面進行參數的判斷, EventBus 只允許訂閱事件中才參數只有一個
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length == 1) {
                //判斷這個方法是否有 @Subscribe 註解
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                if (subscribeAnnotation != null) {
                    //獲得 Event 事件的 Class 對象.
                    Class<?> eventType = parameterTypes[0];
                    //檢查是否需要保存到 findState 的 anyMethodByEventType 中, 返回 true 表示添加成功
                    if (findState.checkAdd(method, eventType)) {
                        //獲得這個方法註解的 ThreadMode 線程模式, 根據不同的模式進行不同的線程調度
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        // 往 findState 用來保存所有的訂閱方法信息集合裏面添加 SubscriberMethod (方法以及註解信息的封裝類) ,
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                    }
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                //拋異常
            }
        } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
           //拋異常
        }
    }
}

這段代碼的執行過程其實並不是很複雜. 在這裏主要是使用了Java的反射和對註解的解析.

  1. 首先通過反射來獲取訂閱者中所有的方法
  2. 遍歷所有方法.
  3. 根據方法的類型, 參數和註解來找到真正的訂閱方法.
  4. 找到訂閱方法後調用 findState.checkAdd(method,eventType) 判斷是否能將訂閱方法相關信息保存到 FindState.subscriberMethods當中.

注: (findState.checkAdd 在上面 1.1.2 FindState 中有詳細描述)

到這裏已經完成對訂閱者中所有訂閱方法的查找. 接着再回到 1.1findUsingInfo()中, 看到findUsingInfo()while循環中還調用了一個方法, 就是FindState.moveToSuperclass().


1.1.5 FindState.moveToSuperclass()

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
      //從 findState 池中獲取 findState 對象, 池中沒有就創建一個新的對象
      FindState findState = prepareFindState();
      //傳入訂閱者的 Class 對象, 爲 findState 做一些初始化工作
      findState.initForSubscriber(subscriberClass);
      //循環直到 findState 的 clazz 爲空( clazz 爲 java. 或 android. 或 javax. 等系統類則結束查找). 
      while (findState.clazz != null) {
          //如果沒有配置 MyEventBusIndex 索引類的情況, 這裏默認基本都是返回 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 中保存所有訂閱方法信息的集合中
                      findState.subscriberMethods.add(subscriberMethod);
                  }
              }
          } else {
              //在運行時利用反射來獲取所有被 @Subscribe 註解標註的訂閱方法
              findUsingReflectionInSingleClass(findState);
          }
          findState.moveToSuperclass();
      }
      //資源回收,並返回用來保存所有的訂閱方法信息的集合(當前訂閱者 class 對象中所有的訂閱方法集合)  
      return getMethodsAndRelease(findState);
  }
      //將成員變量clazz指向clazz的父類並排除系統類
      void moveToSuperclass() {
          if (skipSuperClasses) {
              clazz = null;
          } else {
              clazz = clazz.getSuperclass();
              String clazzName = clazz.getName();
              if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") ||clazzName.startsWith("android.") || clazzName.startsWith("androidx.")) {
                  //排除系統類
                  clazz = null;
              }
          }
      }

這裏的邏輯就是 在第一次循環的時候, FindState.clazz 指向的就是當前訂閱者類, 第一次循環結束的時候, 調用 moveToSuperclass() 如果當前訂閱者有非系統類的父類, 就將 FindState.clazz指向父類, 開始第二次循環, 接着查找父類中所有的訂閱方法, 依次類推.

獲取最後又調用了getMethodsAndRelease(findState) 方法並返回. 見名知意, 獲取所有的方法並釋放.


1.1.6 SubscriberMethodFinder. getMethodsAndRelease(FindState)

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
    //先獲取 findState 中用來保存所有的訂閱方法信息的集合
    List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
    //回收重置 findState.
    findState.recycle();
    //重置 findState 對象池
    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;
}

getMethodsAndRelease就先獲取FindState.subscriberMethods, 接着將FindState中的內容及數據重置, 最後重置FindState對象池.以便複用. 最後返回 subscriberMethods.


至此, EventBus.getDefault().register(this) 註冊方法的第一步獲取訂閱者內所有的訂閱方法就分析完了, 下一章將會遍歷這些方法, 依次對其進行訂閱.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章