Android事件總線(四)源碼解析otto

前言

上一篇文章中講到了otto的用法,這一篇我們來講一下otto的源碼。可能有人覺得otto過時了,但是通過源碼我們學(xué)習(xí)的是高手設(shè)計otto時的設(shè)計理念,這種設(shè)計理念是不過時的。

otto各個類的作用

首先先來看一下otto的源碼的各個類的作用,如下圖所示。


如圖所示,otto的源碼并不多,主要的類的功能如下:

  • Produce、Subscribe:發(fā)布者和訂閱者注解類。
  • Bus:事件總線類,用來注冊和取消注冊,維護(hù)發(fā)布-訂閱模型,并處理事件調(diào)度分發(fā)。
  • HandlerFinder、AnnotatedHandlerFinder:用來查找發(fā)布者和訂閱者。
  • EventProducer、EventHandler:分別封裝發(fā)布者和訂閱者的數(shù)據(jù)結(jié)構(gòu)。

otto構(gòu)造函數(shù)

在使用otto時,首先要創(chuàng)建Bus類,Bus類的構(gòu)造函數(shù)如下所示。

public Bus() {
    this(DEFAULT_IDENTIFIER);
  }

這個DEFAULT_IDENTIFIER是一個字符串"default",this調(diào)用了Bus類的另一個構(gòu)造函數(shù):

 public Bus(String identifier) {
    this(ThreadEnforcer.MAIN, identifier);
  }

ThreadEnforcer.MAIN意味著默認(rèn)在主線程中調(diào)度事件,再往里看this又調(diào)用了什么,如下所示。

  public Bus(ThreadEnforcer enforcer, String identifier) {
    this(enforcer, identifier, HandlerFinder.ANNOTATED);
  }

第一個參數(shù)我們提到了,就是事件調(diào)度的簡稱,identifier為Bus的名稱,默認(rèn)為"default"。而identifier則是HandlerFinder,用于在register、unregister的時候?qū)ふ宜械膕ubscriber和producer。再往里查看this又調(diào)用了什么,如下所示。

  Bus(ThreadEnforcer enforcer, String identifier, HandlerFinder handlerFinder) {
    this.enforcer =  enforcer;
    this.identifier = identifier;
    this.handlerFinder = handlerFinder;
  }

這個是最終調(diào)用的Bus的構(gòu)造函數(shù),在這里要首先記住handlerFinder 指的就是傳進(jìn)來的HandlerFinder.ANNOTATED,后面在注冊時會用到handlerFinder這個屬性。

注冊

生成bus類后,我們要調(diào)用它的register方法來進(jìn)行注冊。register方法如下所示。

public void register(Object object) {
    if (object == null) {
      throw new NullPointerException("Object to register must not be null.");
    }
    enforcer.enforce(this);
    Map<Class<?>, EventProducer> foundProducers = handlerFinder.findAllProducers(object);//1
...
}

注釋1出調(diào)用了handlerFinder的findAllProducers方法,此前講到構(gòu)造函數(shù)時,我們知道這個handlerFinder指的是HandlerFinder.ANNOTATED,ANNOTATED的代碼如下所示。

 HandlerFinder ANNOTATED = new HandlerFinder() {
    @Override
    public Map<Class<?>, EventProducer> findAllProducers(Object listener) {
      return AnnotatedHandlerFinder.findAllProducers(listener);
    }
    @Override
    public Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener) {
      return AnnotatedHandlerFinder.findAllSubscribers(listener);
    }
  };

從上面的代碼的findAllProducers方法和findAllSubscribers方法的返回值可以推斷出一個注冊類只能有一個發(fā)布者,卻可以有多個訂閱者。findAllProducers方法最終調(diào)用的是AnnotatedHandlerFinder的findAllProducers方法:

  static Map<Class<?>, EventProducer> findAllProducers(Object listener) {
    final Class<?> listenerClass = listener.getClass();
    Map<Class<?>, EventProducer> handlersInMethod = new HashMap<Class<?>, EventProducer>();
    Map<Class<?>, Method> methods = PRODUCERS_CACHE.get(listenerClass);//1
    if (null == methods) {
      methods = new HashMap<Class<?>, Method>();
      loadAnnotatedProducerMethods(listenerClass, methods);//2
    }
    if (!methods.isEmpty()) {
      for (Map.Entry<Class<?>, Method> e : methods.entrySet()) {//3
        EventProducer producer = new EventProducer(listener, e.getValue());
        handlersInMethod.put(e.getKey(), producer);
      }
    }
    return handlersInMethod;
  }

PRODUCERS_CACHE是一個ConcurrentHashMap,它的key為bus.register()時傳入的class,而value是一個map,這個map的key是事件的class,value是生產(chǎn)事件的方法。注釋1處首先在PRODUCERS_CACHE根據(jù)傳入的對象的類型查找是否有緩存的事件方法,如果沒有就調(diào)用注釋2處的代碼利用反射去尋找所有使用了@Produce注解的方法,并且將結(jié)果緩存到PRODUCERS_CACHE中。接著在注釋3處遍歷這些事件方法,并為每個事件方法創(chuàng)建了EventProducer類,并將這些EventProducer類作為value存入handlersInMethod并返回。接下來我們返回register方法。如下所示。

  public void register(Object object) {
    if (object == null) {
      throw new NullPointerException("Object to register must not be null.");
    }
    enforcer.enforce(this);
    Map<Class<?>, EventProducer> foundProducers = handlerFinder.findAllProducers(object);
    for (Class<?> type : foundProducers.keySet()) {
      final EventProducer producer = foundProducers.get(type);
      EventProducer previousProducer = producersByType.putIfAbsent(type, producer);//1
      if (previousProducer != null) {
        throw new IllegalArgumentException("Producer method for type " + type
          + " found on type " + producer.target.getClass()
          + ", but already registered by type " + previousProducer.target.getClass() + ".");
      }
      Set<EventHandler> handlers = handlersByType.get(type);
      if (handlers != null && !handlers.isEmpty()) {
        for (EventHandler handler : handlers) {
          dispatchProducerResultToHandler(handler, producer);//2
        }
      }
    }
    ...
  }

調(diào)用完findAllProducers方法后,會在注釋1處檢查是否有該類型的發(fā)布者已經(jīng)存在,如果存在則拋出異常,不存在則調(diào)用注釋2處的dispatchProducerResultToHandler方法來觸發(fā)和發(fā)布者對應(yīng)的訂閱者來處理事件。接下來register方法的后一部分代碼就不帖上來了,跟此前的流程大致一樣就是調(diào)用findAllSubscribers方法來查找所有使用了@Subscribe注解的方法,跟此前不同的是一個注冊類可以有多個訂閱者,接下來判斷是否有該類型的訂閱者存在,也就是判斷注冊類是否已經(jīng)注冊,如果存在則拋出異常,不存在則查找是否有和這些訂閱者對應(yīng)的發(fā)布者,如果有的話,就會觸發(fā)對應(yīng)的訂閱者處理事件。

發(fā)送事件

我們會調(diào)用Bus的post方法來發(fā)送事件,它的代碼如下所示。

  public void post(Object event) {
    if (event == null) {
      throw new NullPointerException("Event to post must not be null.");
    }
    enforcer.enforce(this);
    Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass());//1
    boolean dispatched = false;
    for (Class<?> eventType : dispatchTypes) {
      Set<EventHandler> wrappers = getHandlersForEventType(eventType);
      if (wrappers != null && !wrappers.isEmpty()) {
        dispatched = true;
        for (EventHandler wrapper : wrappers) {
          enqueueEvent(event, wrapper);//2
        }
      }
    }
    if (!dispatched && !(event instanceof DeadEvent)) {
      post(new DeadEvent(this, event));
    }
    dispatchQueuedEvents();//3
  }

注釋1處的flattenHierarchy方法首先會從緩存中查找傳進(jìn)來的event(消息事件類)的所有父類,如果沒有則找到event的所有父類并存儲入緩存中。接下來遍歷這些父類找到它們的所有訂閱者,并在注釋2處將這些訂閱者壓入線程的事件隊列中。并在注釋3處調(diào)用dispatchQueuedEvents方法依次取出事件隊列中的訂閱者來處理相應(yīng)event的事件。

取消注冊

取消注冊時,我們會調(diào)用Bus的unregister方法,unregister方法如下所示。

  public void unregister(Object object) {
    if (object == null) {
      throw new NullPointerException("Object to unregister must not be null.");
    }
    enforcer.enforce(this);
    Map<Class<?>, EventProducer> producersInListener = handlerFinder.findAllProducers(object);//1
    for (Map.Entry<Class<?>, EventProducer> entry : producersInListener.entrySet()) {
      final Class<?> key = entry.getKey();
      EventProducer producer = getProducerForEventType(key);
      EventProducer value = entry.getValue();
      if (value == null || !value.equals(producer)) {
        throw new IllegalArgumentException(
            "Missing event producer for an annotated method. Is " + object.getClass()
                + " registered?");
      }
      producersByType.remove(key).invalidate();//2
    }
  ...
  }

取消注冊分為兩部分,一部分是訂閱者取消注冊,另一部分是發(fā)布者取消注冊。這兩部分的代碼類似,因此,上面的代碼只列出了發(fā)布者取消注冊的代碼。在注釋1處得到所有使用@Produce注解的方法,并遍歷這些方法,調(diào)用注釋2處的代碼從緩存中清除所有和傳進(jìn)來的注冊類相關(guān)的發(fā)布者,來完成發(fā)布者的取消注冊操作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容