EventBus 源码分析

前言

上篇文章笔者简单介绍了 EventBus 的用法。本篇文章笔者将简单分析 EventBus 的源码。从上篇文章我们可以得知,要使用 EventBus 首先要调用 EventBus 的 register 方法注册(一般在 onCreate 方法中)。然后,定义被 Subscribe 注解的方法(用来接收由 post 方法发送的消息)。最后,调用 unregister 方法解注册(一般在 onDestroy 方法中)。所以,笔者注重分析 EventBus 的 register , post 方法的实现,以及 EventBus 是如何调用接收消息的方法的。

register

EventBus.getDefault().register(this);

我们一般调用上述代码进行注册,调用上述代码的类我们暂且称为注册类。其中 getDefault 方法获取 EventBus 的实例。register 方法源码如下:

    public void register(Object subscriber) {
      Class<?> subscriberClass = subscriber.getClass();
      List<SubscriberMethod> subscriberMethods =
        subscriberMethodFinder.findSubscriberMethods(subscriberClass);
      synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
          subscribe(subscriber, subscriberMethod);
        }
      }
    }

先来看 3 , 4 行:这里调用 subscriberMethodFinder.findSubscriberMethods(subscriberClass) 得到一个 SubscriberMethod 的 List 。其中 SubscriberMethod 是对注册类中接收消息的方法的 Method 对象的封装。subscriberMethodFinder 是 SubscriberMethodFinder 类型的,我们接着追踪 SubscriberMethodFinder 的 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;
      }
    }

第 2 行:List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); 其中 METHOD_CACHE 是 Map

    private void findUsingReflectionInSingleClass(FindState findState) {
      Method[] methods;
      try {
        // This is faster than getMethods, especially when subscribers are fat classes like Activities
        methods = findState.clazz.getDeclaredMethods();
      } catch (Throwable th) {
        // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
        methods = findState.clazz.getMethods();
        findState.skipSuperClasses = true;
      }
      for (Method method : methods) {
        int modifiers = method.getModifiers();
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
          Class<?>[] parameterTypes = method.getParameterTypes();
          if (parameterTypes.length == 1) {
            Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
            if (subscribeAnnotation != null) {
              Class<?> eventType = parameterTypes[0];
              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");
        }
      }
    }

由上述代码可以看出,findUsingReflectionInSingleClass 获取注册类中定义的方法并且遍历这些方法,然后把 public 、非 static 、非 abstract 、非 synthetic 、一个参数的、并且被注解 Subscribe 标注的方法封装成 SubscriberMethod 对象。并添加到 List subscriberMethods 中。然后把 subscriberMethods 返回给 findUsingInfo 方法,并最终返回给 EventBus 的 register 方法。接下来我们重新回到 register 方法。
register 方法中第 6 7 8 行 对返回的 List subscriberMethods 的遍历并调用 subscribe 方法。subscribe 方法的源码如下:

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

      if (subscriberMethod.sticky) {
        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);
        }
      }
    }

在 subscribe 方法中,把
subscriberMethod 和注册类对象封装成 Subscription 对象 newSubscription 。 并将 newSubscription 添加到 CopyOnWriteArrayList subscriptions 中并且通过 subscriberMethod.priority 确定 newSubscription 在 subscriptions 中的位置。subscriberMethod.priority 其实就是方法执行的优先级。需要注意的是,subscriptions 会被保存到 subscriptionsByEventType 中,subscriptionsByEventType 是一个 Map ,其 key 是注册类中被注解的方法的参数的类型,也就是接受消息的方法的参数的类型。 至此 register 方法大概介绍完毕。下面来看 post 方法。

post

EventBus 的 post 方法 是 EventBus 用来发布消息的方法。 post 方法被调用后会将 post 方法的参数传递给相应的注册类中的相应的方法中,并调用此方法。这个过程是怎样的呢?请看 post 的源码:

    /** Posts the given event to the event bus. */
    public void post(Object event) {
      PostingThreadState postingState = currentPostingThreadState.get();
      List<Object> eventQueue = postingState.eventQueue;
      eventQueue.add(event);

      if (!postingState.isPosting) {
        postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
        postingState.isPosting = true;
        if (postingState.canceled) {
          throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
          while (!eventQueue.isEmpty()) {
            postSingleEvent(eventQueue.remove(0), postingState);
          }
        } finally {
          postingState.isPosting = false;
          postingState.isMainThread = false;
        }
      }
    }

由上述源码可以看出,第 4 , 5 行把 post 方法的参数添加到一个 List 中。第 8 行得到 post 方法是否是在 mian 线程中调用的。第 14 15 16 行对保存 post 方法参数的 List 遍历删除并调用 postSingleEvent 方法。在 postSingleEvent 方法中会调用 postSingleEventForEventType 方法。postSingleEventForEventType 方法的源码如下:

    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState,
      Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
      subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
      for (Subscription subscription : subscriptions) {
        postingState.event = event;
        postingState.subscription = subscription;
        boolean aborted = false;
        try {
          postToSubscription(subscription, event, postingState.isMainThread);
          aborted = postingState.canceled;
        } finally {
          postingState.event = null;
          postingState.subscription = null;
          postingState.canceled = false;
        }
        if (aborted) {
          break;
        }
      }
      return true;
    }
    return false;
  }

在 postSingleEventForEventType 方法中通过参数的类型获取 subscriptionsByEventType 中相应的 Subscription 的 CopyOnWriteArrayList subscriptions。如果 subscriptions 不为空则遍历它,并调用 postToSubscription 。postToSubscription 的源码如下:

    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
      switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
          invokeSubscriber(subscription, event);
          break;
        case MAIN:
          if (isMainThread) {
            invokeSubscriber(subscription, event);
          } else {
            mainThreadPoster.enqueue(subscription, event);
          }
          break;
        case BACKGROUND:
          if (isMainThread) {
            backgroundPoster.enqueue(subscription, event);
          } else {
            invokeSubscriber(subscription, event);
          }
          break;
        case ASYNC:
          asyncPoster.enqueue(subscription, event);
          break;
        default:
          throw new IllegalStateException(
            "Unknown thread mode: " + subscription.subscriberMethod.threadMode);
      }
    }

由上述代码可以看出,postToSubscription 通过注解 Subscription 的属性 threadMode 的值,决定相应的接受信息的方法在哪个线程里调用。调用的规则已经在上篇文章中阐述过,这里不再赘述。invokeSubscriber 方法其实就是利用反射来实现接受信息的方法的调用的,这里也不再详细说明。

总结

EventBus 的实现其实就是一个典型的观察者模式。注册类向 EventBus 注册,当有消息需要发送的时候 EventBus 通过 post 方法向注册者们分发消息,其实就是遍历调用注册类中符合要求的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值