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

目錄(?)[+]

相關文章 
Android事件總線(一)EventBus3.0用法全解析 
Android事件總線(二)EventBus3.0源碼解析 
Android事件總線(三)otto用法全解析

前言

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

otto各個類的作用

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

如圖所示,otto的源碼並不多,主要的類的功能如下: 
- Produce、Subscribe:發佈者和訂閱者註解類。 
- Bus:事件總線類,用來註冊和取消註冊,維護髮布-訂閱模型,並處理事件調度分發。 
- HandlerFinder、AnnotatedHandlerFinder:用來查找發佈者和訂閱者。 
- EventProducer、EventHandler:分別封裝發佈者和訂閱者的數據結構

otto構造函數

在使用otto時,首先要創建Bus類,Bus類的構造函數如下所示。

public Bus() {
    this(DEFAULT_IDENTIFIER);
  }
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

這個DEFAULT_IDENTIFIER是一個字符串”default”,this調用了Bus類的另一個構造函數:

 public Bus(String identifier) {
    this(ThreadEnforcer.MAIN, identifier);
  }
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

ThreadEnforcer.MAIN意味着默認在主線程中調度事件,再往裏看this又調用了什麼,如下所示。

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

第一個參數我們提到了,就是事件調度的簡稱,identifier爲Bus的名稱,默認爲”default”。而identifier則是HandlerFinder,用於在register、unregister的時候尋找所有的subscriber和producer。再往裏查看this又調用了什麼,如下所示。

  Bus(ThreadEnforcer enforcer, String identifier, HandlerFinder handlerFinder) {
    this.enforcer =  enforcer;
    this.identifier = identifier;
    this.handlerFinder = handlerFinder;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

這個是最終調用的Bus的構造函數,在這裏要首先記住handlerFinder 指的就是傳進來的HandlerFinder.ANNOTATED,後面在註冊時會用到handlerFinder這個屬性。

註冊

生成bus類後,我們要調用它的register方法來進行註冊。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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

註釋1出調用了handlerFinder的findAllProducers方法,此前講到構造函數時,我們知道這個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);
    }
  };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

從上面的代碼的findAllProducers方法和findAllSubscribers方法的返回值可以推斷出一個註冊類只能有一個發佈者,卻可以有多個訂閱者。findAllProducers方法最終調用的是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;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

PRODUCERS_CACHE是一個ConcurrentHashMap,它的key爲bus.register()時傳入的class,而value是一個map,這個map的key是事件的class,value是生產事件的方法。註釋1處首先在PRODUCERS_CACHE根據傳入的對象的類型查找是否有緩存的事件方法,如果沒有就調用註釋2處的代碼利用反射去尋找所有使用了@Produce註解的方法,並且將結果緩存到PRODUCERS_CACHE中。接着在註釋3處遍歷這些事件方法,併爲每個事件方法創建了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
        }
      }
    }
    ...
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

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

發送事件

我們會調用Bus的post方法來發送事件,它的代碼如下所示。

  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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

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

取消註冊

取消註冊時,我們會調用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
    }
  ...
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

取消註冊分爲兩部分,一部分是訂閱者取消註冊,另一部分是發佈者取消註冊。這兩部分的代碼類似,因此,上面的代碼只列出了發佈者取消註冊的代碼。在註釋1處得到所有使用@Produce註解的方法,並遍歷這些方法,調用註釋2處的代碼從緩存中清除所有和傳進來的註冊類相關的發佈者,來完成發佈者的取消註冊操作。

發佈了23 篇原創文章 · 獲贊 12 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章