美文网首页Android开发
EventBus 源码的阅读03 - EventBus 的注册(

EventBus 源码的阅读03 - EventBus 的注册(

作者: __Y_Q | 来源:发表于2020-11-20 11:01 被阅读0次

在上篇中, 分析学习了 EventBus 注册过程中的第一步. 就是获取注册类中所有的订阅方法.
下篇主要分析的是在获得所有订阅方法后, 逐个对他们进行订阅.
那么具体是如何进行订阅的呢. 下面直接进入正题.


EventBus.register(Object subscriber)

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

看代码, 在获得 subscriberMethods 集合后, 就开始了遍历整个集合, 并调用了 subscribe(Object subscriber, SubscriberMethod subscriberMethod) 方法. 传入订阅者与订阅者内部的某个订阅方法这两个参数, 准备开始订阅.


2. EventBus.subscribe(subscriber, subscriberMethod)

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    //分析一
    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();
    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);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);
     //分析 5
    if (subscriberMethod.sticky) {
        if (eventInheritance) {
            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);
        }
    }
}

因为代码过长, 所以讲 subscribe 方法分为两个部分来分析. 第一部分是正常处理流程, 第二部分是粘性事件的处理流程.

开始分析第一部分之前, 再回忆一下在第一章中创建 EventBus 对象中的两个重要的 Map 集合, 以便加深印象, 也更容易加深对本篇内容的理解.
(这里就不再贴图了, 直接以文字描述了. 想看图的可以去 EventBus 源码的阅读01 - EventBus 对象的创建 中查看.)

  • private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType
    以当前 Event 事件为 key, 以 CopyOnWriteArrayList<Subscription> 列表为 value.
    Subscription 可以理解为一条订阅记录, 这个记录中又包含了两个内容, 一个是包含了订阅这个 Event 事件的 subscriber 订阅者(反射执行对象),一个是 SubscriberMethod. (订阅方法的包装类.)

这个 map 的目的是为了在发送 Event 事件的时候, 以 Event 事件为 key 直接在这个 map 中找到所有订阅了这个事件的 Subscription(订阅记录)列表. 找到列表后, 就可以针对列表内的每一条订阅记录进行发送事件.

  • private final Map<Object, List<Class<?>>> typesBySubscriber
    subscriber 订阅者为 key, 以订阅者中订阅的所有 Event 事件类型列表为 value.

订阅者到事件列表的 map, 这样订阅者向 EventBus 注册与解除注册的时候可以根据订阅者获取该订阅者订阅的所有事件, 对每个事件分别进行操作.

  • 分析一
    1. 先获取订阅方法的事件类型, 也就是订阅方法的参数 eventType.

    2. 接着根据方法传入的参数 subscriber 订阅者及 subscriberMethod 订阅方法, 创建一条订阅记录newSubscription.

    3. 以在 1 处获得的事件类型 eventTypekey, 从 subscriptionsByEventType 中获取到所有订阅了这个事件的订阅记录列表 subscriptions.


  • 分析二
    1. 判断获取到的订阅记录列表 subscriptions 是否为null, 也就是说判断缓存中有没有当前事件的订阅记录.
      Ps: 默认第一次执行这里是为 null, 但是如果是使用了索引类编译时注解处理的方式, 这里就会有值.

    2. 如果为 null, 说明没有任何订阅记录, 那就创建一个新的订阅记录列表 subscriptions, 并将这个订阅记录列表添加到 subscriptionsByEventType 中.

    3. 如果不为 null, 说明当前事件有被订阅过, 但是不知道是不是当前订阅者订阅的, 有可能是别的订阅者也订阅了这个事件. 那么接着判断是否存在相同的订阅记录, 存在则直接抛出异常. 当前订阅者已经注册过这个事件. 属于重复订阅.
      Ps: 注意, 这里只是处理了订阅记录列表 subscriptions 中包含了 newSubscription 的情况, 并没有处理没有包含的情况, 也没有将 newSubscription 添加到 subscriptions 中. 在分析 3 中才会添加进去.


  • 分析三

    1. 获取订阅了当前事件的订阅记录列表 subscriptions的长度.

    2. 遍历订阅记录列表 subscriptions, 根据优先级将我们在 2 中创建的单条订阅记录 newSubscription, 插入到指定位置.

    至此, 已经将我们需要缓存的当前事件的订阅记录 newSubscription放到了 subscriptions列表中. 而 subscriptions列表又存在于 subscriptionsByEventType 这个 map 的与当前事件对应的 value 中.
    所以也可以简单的理解为, 我们往 subscriptionsByEventTypemap 中存放了一条数据. key 就是当前事件, value 是一个列表, 这个列表中包含了所有订阅了这个事件的订阅记录.


  • 分析四
    1. 先根据 subscriber 订阅者从 typesBySubscriber 中获取当前订阅者中所有的订阅事件类型列表 subscribedEvents. 接着对这个列表进行判断. 如果没有那么就创建一个订阅事件类型列表 subscribedEvents, 并将这个列表放入到 typesBySubscriber 这个 map 中. key为当前订阅者.
    2. 接着将当前的订阅事件类型存入到 subscribedEvents列表中.

  • 分析五
    1. 判断订阅方法是否为粘性事件. 如果不是粘性事件就直接执行完毕, 就为当前订阅者的订阅方法订阅完毕, 返回到 EventBus.register 方法的循环中, 继续为下一个订阅方法订阅.
    2. 如果订阅方法是粘性事件, 但是不支持继承关系. (默认为 true). 则直接就调用 checkPostStickyEventToSubscription 方法开始分发本次粘性事件.
    3. 如果订阅方法是粘性事件, 同时 eventInheritance = true, 表示支持继承关系. 那么就获取所有粘性事件的 Set 集合. 并遍历这个集合. 依次调用 checkPostStickyEventToSubscription 方法分发集合中的每一个粘性事件.

到这里 EventBus 的注册流程分析完了. 总结后大致流程如下:

  1. 根据 EventBus.getDefault().register(this) 中的这个 this, 作为订阅者.
  2. 根据订阅者获取订阅者内部的所有订阅方法.
  3. 接着遍历订阅者内部的所有订阅方法, 进行订阅.
  4. 订阅的时候会先判断是否订阅过这个事件.
  5. 按照优先级将订阅记录加入到 subscriptionsByEventType 的 value 的 list 中
  6. 将当前事件类型加入到订阅者对应的订阅事件列表中 (typesBySubscriber 的 value 的 list 中)
  7. 是否是粘性事件.
  8. 调用 checkPostStickyEventToSubscription 分发事件.

相关文章

网友评论

    本文标题:EventBus 源码的阅读03 - EventBus 的注册(

    本文链接:https://www.haomeiwen.com/subject/errybktx.html