前言
上一篇文章中講到了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ā)布者的取消注冊操作。