美文网首页
EventBus原理剖析

EventBus原理剖析

作者: pphdsny | 来源:发表于2018-01-22 22:37 被阅读242次

    EventBus

    EventBus 是一个 Android 事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递。传统的事件传递方式包括:Handler、BroadCastReceiver、Interface 回调,相比之下 EventBus 的优点是代码简洁,使用简单,并将事件发布和订阅充分解耦。

    下列源码基于EventBus3.0.0进行分析

    Subscribe

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface Subscribe {
      //处理事件线程
        ThreadMode threadMode() default ThreadMode.POSTING;
    
        /**
         * If true, delivers the most recent sticky event (posted with
         * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
         */
      //是否粘性,详见postSticky
        boolean sticky() default false;
    
        /** Subscriber priority to influence the order of event delivery.
         * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
         * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
         * delivery among subscribers with different {@link ThreadMode}s! */
      //优先级
        int priority() default 0;
    }
    

    Register

    public void register(Object subscriber) {
      //获取类中使用@Subscribe字段的方法列表
      //既然不是APT,不过也能理解,这部分是动态去注册的
      Class<?> subscriberClass = subscriber.getClass();
      List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
      synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
          //添加到数组缓存中
          subscribe(subscriber, subscriberMethod);
        }
      }
    }
    //findSubscriberMethods
    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
      //METHOD_CACHE缓存,防止重新找,提高性能
      List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
      if (subscriberMethods != null) {
        return subscriberMethods;
      }
      //遍历方法和注解得到注册了方法列表
      //通过Class做到其所有的方法,再找到其使用了Subscribe注解的方法
      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;
      }
    }
    

    Post

    public void post(Object event) {
      //获取当前线程,将message添加到消息队列中
      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;
        }
      }
    }
    

    postSingleEvent

    拆分事件类型(包括事件、事件父类、实现接口)

    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
      Class<?> eventClass = event.getClass();
      boolean subscriptionFound = false;
      //事件是否继承,默认为true,也就是说会发送一个事件及其父类和接口的所以事件类
      if (eventInheritance) {
        //找到Event以及Event的父类和其所实现的接口
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) {
          Class<?> clazz = eventTypes.get(h);
          subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
        }
      } else {
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
      }
      if (!subscriptionFound) {
        if (logNoSubscriberMessages) {
          Log.d(TAG, "No subscribers registered for event " + eventClass);
        }
        //如果发送一个没有被注册过的事件,会默认发送一个NoSubscriberEvent
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
            eventClass != SubscriberExceptionEvent.class) {
          post(new NoSubscriberEvent(this, event));
        }
      }
    }
    

    postSingleEventForEventType

    找到具体的消费类,进行事件消费

    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
      //找到注册此event的事件类列表,register时候将其放进去
      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;
    }
    

    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 {
            //不在主线程发送,则通过Handler继续消费
            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);
      }
    }
    

    对ThreadMode源码分析可知:

    1. PostThread`:默认的 ThreadMode,表示在执行 Post 操作的线程直接调用订阅者的事件响应方法,不论该线程是否为主线程(UI 线程)。当该线程为主线程时,响应方法中不能有耗时操作,否则有卡主线程的风险。适用场景:对于是否在主线程执行无要求,但若 Post 线程为主线程,不能耗时的操作
    2. MainThread:在主线程中执行响应方法。如果发布线程就是主线程,则直接调用订阅者的事件响应方法,否则通过主线程的 Handler 发送消息在主线程中处理——调用订阅者的事件响应函数。显然,MainThread类的方法也不能有耗时操作,以避免卡主线程。适用场景:必须在主线程执行的操作
    3. BackgroundThread:在后台线程中执行响应方法。如果发布线程不是主线程,则直接调用订阅者的事件响应函数,否则启动唯一的后台线程去处理。由于后台线程是唯一的,当事件超过一个的时候,它们会被放在队列中依次执行,因此该类响应方法虽然没有PostThread类和MainThread类方法对性能敏感,但最好不要有重度耗时的操作或太频繁的轻度耗时操作,以造成其他操作等待。适用场景:操作轻微耗时且不会过于频繁,即一般的耗时操作都可以放在这里;
    4. Async:不论发布线程是否为主线程,都使用一个空闲线程来处理。和BackgroundThread不同的是,Async类的所有线程是相互独立的,因此不会出现卡线程的问题。适用场景:长耗时操作,例如网络访问

    postSticky

    发送粘性事件

    public void postSticky(Object event) {
      synchronized (stickyEvents) {
        //将事件缓存到stickyEvents中,当再次register时候,会去stickyEvents中获取是否存在,如已经缓存则直接消费
        //使用场景:如果您正在跟踪用户的位置,或简单的缓存数据,跟踪电池电量等,您可以使用粘性事件。
        stickyEvents.put(event.getClass(), event);
      }
      // Should be posted after it is putted, in case the subscriber wants to remove immediately
      post(event);
    }
    //register中调用subscribe
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
      ...略
      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);
        }
      }
    }
    

    Priority

    优先级的应用:高优先级的事件处理函数可以终止事件的传递,通过cancelEventDelivery方法,但有一点需要注意,这个事件的ThreadMode必须是PostThread,并且只能终于它在处理的事件。

     private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
       //...略
         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;
         }
       }
       //...略
     }
    

    cancelEventDelivery

    终止事件分发

    public void cancelEventDelivery(Object event) {
      PostingThreadState postingState = currentPostingThreadState.get();
      
      if (!postingState.isPosting) {//事件必须在发送中
        throw new EventBusException(
          "This method may only be called from inside event handling methods on the posting thread");
      } else if (event == null) {//事件必须非null
        throw new EventBusException("Event may not be null");
      } else if (postingState.event != event) {//事件必须是当前正在处理
        throw new EventBusException("Only the currently handled event may be aborted");
      } else if (postingState.subscription.subscriberMethod.threadMode != ThreadMode.POSTING) {
        //事件必须在发送线程中处理
        throw new EventBusException(" event handlers may only abort the incoming event");
      }
      //canceld标示置为true,postSingleEventForEventType中进行中断
      postingState.canceled = true;
    }
    

    注意事项

    1. 如消费事件的线程是在主线程,不宜做耗时操作
    2. post的事件会连着其父类和所实现的接口一起分发
    3. register&unregister一定要配对出现,否则会引发内存泄露

    相关文章

      网友评论

          本文标题:EventBus原理剖析

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