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) 注册方法的第一步获取订阅者内所有的订阅方法就分析完了, 下一章将会遍历这些方法, 依次对其进行订阅.

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