当把EventBus比喻成苹果卖手机

概况

EventBus一直都知道这个库,但是没有接触,看到国内 Top500 Android 应用分析报告使用情况,觉得有必要掌握此库。EventBus是android的一个用于消息传递的库,属于订阅者模式,让发布者和订阅者解耦。本文通过对EventBus的使用,分析其内部实现。

简单使用

关于EventBus的使用不是本文的重点,EventBus的使用请查看EventBus使用详解

做个比喻

为了更好的理解EventBus这个库的流程,以下将根据下图做一个比喻。
new Module...
如上图,EventBus充当苹果公司销售部的角色。当苹果公司(Publisher发布者)生产(调用post方法)出某一款手机(EventType)时,消息会传递到销售部,由销售部告诉用户(Subscriber订阅者)。例如用户想购买iphone8,该用户必须要预约(方法被@Subscriber注解),当销售部接到公司说iphone8开始售卖了,那么只有预约手机的用户可以购买(订阅方法被调用)。

阅读源码

EventBus.getDefault().register(this);

public static EventBus getDefault() {
     if (defaultInstance == null) {
         synchronized (EventBus.class) {
             if (defaultInstance == null) {
                 defaultInstance = new EventBus();  //调用默认构造函数构建单例
             }
         }
     }
     return defaultInstance;
 }

看new EventBus做了什么初始化工作:

public EventBus() {
        this(DEFAULT_BUILDER);
    }

空构造函数引用带参数的构造函数,参数类型是EventBusBuilder:

EventBus(EventBusBuilder builder) {
       subscriptionsByEventType = new HashMap<>();
       typesBySubscriber = new HashMap<>();          
       stickyEvents = new ConcurrentHashMap<>();     
       mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);  
       backgroundPoster = new BackgroundPoster(this);                    
       asyncPoster = new AsyncPoster(this);            
       indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;    
       subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
               builder.strictMethodVerification, builder.ignoreGeneratedIndex);
       logSubscriberExceptions = builder.logSubscriberExceptions;   
       logNoSubscriberMessages = builder.logNoSubscriberMessages;
       sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
       sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
       throwSubscriberException = builder.throwSubscriberException;
       eventInheritance = builder.eventInheritance;
       executorService = builder.executorService;
   }

通过使用默认构造器EventBusBuilder,默认配置了一些参数,部分重点参数会接下来在阅读源码中提到。

register(Object subscriber)

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

调用register方法,可以看到参数是一个Object,EventBus支持Activity、Fragment、Service、BroadcastReceiver等之间传递消息。
调用subscriber.getClass()返回subscriber对象的运行时类的Java.lang.Class。
根据subscriber运行时的类获取该类所以的SubscriberMethod,就是该类被@Subscriber注解的所有方法。
通过循环该类所有的的被注解的方法,通过调用subscriber(subscriber, subscriberMethod)方法绑定subscriber和subscriberMethod的关系。

subscribe(subscriber, subscriberMethod);

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    //获取subsrciberMethod传递的自定义EventType参数的运行时的类
    Class<?> eventType = subscriberMethod.eventType;
    //Subscription用于绑定subscriber和sucriberMethod,一个订阅者可以有多个subscriberMethod
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //根据EventType的运行时类取到该类所有的subscriptioins,subscriptionsByEventType是HashMap中的key
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
         subscriptions = new CopyOnWriteArrayList<>();
         //若根据EventType找不到subscriptions,则eventType作key,subscriptions作value添加到subscriptionByEventType中。
         subscriptionsByEventType.put(eventType, subscriptions);
    } else {
         if (subscriptions.contains(newSubscription)) {
         //已经存在newSubscription,抛出异常该订阅者已经注册,不可重复注册同一个subscriber
             throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                     + eventType);
         }
    }

    int size = subscriptions.size();
    for (int i = 0; i <= size; i++) {
        //循环subscriptions,根据标记优先级的priority从高到低,将新的subscription插入到subscriptions中
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }
    //typesBySubscriber是一个HashMap,根据subscriber做key,获取该subscriber对应的所有的订阅事件的类型
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
      if (subscribedEvents == null) {
          subscribedEvents = new ArrayList<>();
          //该订阅者之前的订阅事件类型列表为空,则将当前订阅类型添加到typesBySubscriber中
          typesBySubscriber.put(subscriber, subscribedEvents);
      }
    subscribedEvents.add(eventType);
    //如果该方法被标识为sticky事件
    if (subscriberMethod.sticky) {
         if (eventInheritance) { eventInheritance标识是否考虑EventType的类层次结构
              //循环所有的sticky黏性事件
              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();
                     heckPostStickyEventToSubscription(newSubscription, stickyEvent);
                  }
              }
         } else {
             Object stickyEvent = stickyEvents.get(eventType);
             checkPostStickyEventToSubscription(newSubscription, stickyEvent);
         }
    }
}

关键代码以上都有注释。
黏性事件,主要使用场景是:当订阅者尚未创建,先调用EventBus.getDefault().postSticky()方法发送一个sticky事件,该事件会被stickyEvents缓存起来,当订阅该事件的类调用register()方法时,同样可以收到该事件。而调用EventBus.getDefault().post()则必须先调用register(),才能收到事件。

checkPostStickyEventToSubscription(newSubscription, stickyEvent);

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
            // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
            // --> Strange corner case, which we don't take care of here.
            postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
        }
    }

跳转postToSubscription方法;

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
       //根据@subscriber中threadMode进行区分,POSTING为当前线程执行,
       //MAIN为主线程,BACKGROUND为子进程,ASYNC为异步执行。
       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);
       }
   }

invokeSubscriber(subscription, event)

下面我们来分析threadMode = ThreadMode.POSTING的情况,调用invokeSubscriber()方法,其他情况类似。

void invokeSubscriber(Subscription subscription, Object event) {
        try {
            //反射机制执行订阅的方法
            subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
        } catch (InvocationTargetException e) {
            handleSubscriberException(subscription, event, e.getCause());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Unexpected exception", e);
        }
    }

EventBus.getDefault().register(this)小结

回到苹果公司卖手机的例子,EventBus.getDefault().register(this)中,this指的就是买手机的顾客,顾客通过调用此方法告诉销售部说我想登记买手机,登记的流程包括:
A.找到该用户所有的购买手机型号的预约信息(根据subscriber找到所有subscriberMethorssubscriberMethodFinder.findSubscriberMethods(subscriberClass));
B.将新登记的预约信息和之前的预约信息做比对,如果之前没有预约信息subscriptions == null,则添加到预约信息中;若之前有预约过的信息,检查是否有相同的预约信息(subscriber和subscriberMethod都一样)subscriptions.contains(newSubscription),若有,则预约失败,并且丢给用户一个臭脸throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "+ eventType);
C.添加到预约信息列表的时候,根据优先级由高到低,优先级相同插入到最后边的原则放入列表。
D.若用户想预约的手机不是新款手机,而是一款老手机subscriberMethod.sticky=ture,则无需预约,可直接购买。

EventBus.getDefault().post(EventType);

post()和postSticky()的区别

发布者调用post(EventType)方法,若订阅者需要接收到此EventType,则订阅者必须要先注册并且注解接收此方法,否则收不到该方法;而调用postSticky(EventType),及时先发布者先发送消息,订阅者在注册事件,也能收到消息。
例如苹果公司出iphone8新款手机了,那么用户需要先预约手机,不预约的不能购买,此时苹果公司调用post(iphone8)方法;如果苹果公司推出的是一款旧手机iphone5,iphone5无须预约,顾客到店就可以直接购买,这时候调用postSticky(iphone5)方法。

post(EventType);

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

将当前发送事件添加到待处理的事件列表,如果当前不是在发送事件状态,则循环事件列表,调用postSingleEvent(eventQueue.remove(0), postingState)发送每一个事件。

postSingleEvent(eventQueue.remove(0), postingState);

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
       Class<?> eventClass = event.getClass();
       boolean subscriptionFound = false;
       //当需要处理事件的继承关系时
       if (eventInheritance) {
           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);
           }
           //如果该事件没有subscriber
           if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                   eventClass != SubscriberExceptionEvent.class) {
               post(new NoSubscriberEvent(this, event));
           }
       }
   }

主要处理调用postSingleEventForEventType()遇到的错误。

postSingleEventForEventType()

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
        //根据eventType运行时的类找到所有的subscripitions
            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;
    }

以上代码主要是将该事件传递给订阅了该事件的订阅者,每一个订阅者都尝试将该事件作为参数,调用所有的订阅的方法。

post()方法小结

以上逻辑并不复杂,就直接做比喻好了:
苹果公司发布iphone8的流程:
A.在发布iphone8的时候,先检查一下iphone8之前的手机是否都已经发布了,将iphone8添加到发布列表中;
B.若当前没有手机在发布流程中,则从发布列表中取出接下来待发布的手机,假设iphone8之前的都已经发布,那iphone8就进入发布流程;
C.根据iphone8找出所有iphone8的预约信息,预约了iphone8的用户可以购买手机;
D.处理特殊的情况:如果没有人预约,或者之前预约的人不购买。

总结

以上分析了EventBus的两个主要入口代码,做的比喻可能并不恰当,但应该能对理解源码的逻辑有很大的帮助。

引用

EventBus 源码解析
EventBus 3.0 源代码分析