標籤:
一、Otto簡單介紹
OTTO是Square推出的庫,地址:https://github.com/square/otto
先來看看otto的官方介紹
An enhanced Guava-based event bus with emphasis on Android support.Otto is an event bus designed to decouple different parts of your application while still allowing them to communicate efficiently.Forked from Guava, Otto adds unique functionality to an already refined event bus as well as specializing it to the Android platform.
OTTO基於Guava項目的Android支援庫,如果你在Android程式開發的過程中想要不同的組件之間進行有效通訊可以使用這個庫。通過otto庫可以。
二、Otto簡單使用1、建立一個BUS的單例。
public class AppConfig {private static final Bus BUS = new Bus();public static Bus getInstance() { return BUS;}}2、在需要使用Otto的類中註冊
在類建立好之後,或者需要重新註冊的時候註冊,一般在Activity的onCreate()或者onPause()方法中
AppConfig.getBusInstance().register(this);
3、定義訂閱者法
@Subscribepublic void onWallpaperUpdate(MyObject obj) { //對obj進行需要的邏輯處理}<span style="font-family:SimSun;"></span>
4、發送訊息
AppConfig.getBusInstance().post(myobj);
5、解除綁定
注意在類銷毀的時候或者暫時不需要再收訊息的時候解除綁定,,一般在Activity的onDestroy()或者onResume()方法中
AppConfig.getBusInstance().unregister(this);
三、Otto源碼解析1、整體結構otto的源碼結構非常簡單,所有類都包含在 com.squareup.otto這一個包中,不計內部類,只有9個類跟介面,分別是 AnnotatedHandlerFinder 註解解析器
Bus 匯流排核心類
DeadEvent 沒有接收者的事件
EventHandler 事件訂閱者
EventProducer 事件生產者
HandlerFinder 擷取接收者生產者
Produce 生產者的註解
Subscribe 訂閱者註解
ThreadEnforcer對線程進行校正
2、Bus的關鍵屬性方法分析(1)建構函式
public Bus() { this(DEFAULT_IDENTIFIER); } public Bus(String identifier) { this(ThreadEnforcer.MAIN, identifier); } public Bus(ThreadEnforcer enforcer) { this(enforcer, DEFAULT_IDENTIFIER); } public Bus(ThreadEnforcer enforcer, String identifier) { this(enforcer, identifier, HandlerFinder.ANNOTATED); } Bus(ThreadEnforcer enforcer, String identifier, HandlerFinder handlerFinder) { this.enforcer = enforcer; this.identifier = identifier; this.handlerFinder = handlerFinder; }我們通常使用Bus()這個構造方法,在整個app中建立一個單例,這樣不但節省資源,更重要的是保證訊息正常到達如果不是單例,用一個Bus進行了註冊,而用另外一個Bus發送訊息,這樣訂閱的方法是無法收到訊息的。enforcer是對線程進行校正,有兩個取值,一個是ThreadEnforcer.ANY,另一個是ThreadEnforcer.MAIN,預設值是ThreadEnforcer.MAIN,這樣只能在主線程進行訊息處理。如果在非主線程註冊或者發送訊息,就會拋出異常throw new IllegalStateException("Event bus " + bus + " accessed from non-main thread " + Looper.myLooper());這點一定要注意:
Otto預設構造方法建立的Bus執行個體只能在主線程調用如果要在其他線程使用,就使用ThreadEnforcer.ANY,或者自訂ThreadEnforcer。只在主線程使用,能保證簡潔,不混亂。但是我們實際使用中很多時候還是要跨線程通訊的。identifier是一個標識,在Bus的toString()方法中會用到。
HandlerFinder用來解析註冊的對象,預設的實現是HandlerFinder.ANNOTATED,使用註解解析
(2)對象註冊
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); //checking if the previous producer existed 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); } } } Map<Class<?>, Set<EventHandler>> foundHandlersMap = handlerFinder.findAllSubscribers(object); for (Class<?> type : foundHandlersMap.keySet()) { Set<EventHandler> handlers = handlersByType.get(type); if (handlers == null) { //concurrent put if absent Set<EventHandler> handlersCreation = new CopyOnWriteArraySet<EventHandler>(); handlers = handlersByType.putIfAbsent(type, handlersCreation); if (handlers == null) { handlers = handlersCreation; } } final Set<EventHandler> foundHandlers = foundHandlersMap.get(type); if (!handlers.addAll(foundHandlers)) { throw new IllegalArgumentException("Object already registered."); } } for (Map.Entry<Class<?>, Set<EventHandler>> entry : foundHandlersMap.entrySet()) { Class<?> type = entry.getKey(); EventProducer producer = producersByType.get(type); if (producer != null && producer.isValid()) { Set<EventHandler> foundHandlers = entry.getValue(); for (EventHandler foundHandler : foundHandlers) { if (!producer.isValid()) { break; } if (foundHandler.isValid()) { dispatchProducerResultToHandler(foundHandler, producer); } } } } }對象註冊首先進行了非空校正,然後是線程的校正,
對象不可為空白,不可多次註冊在註冊對象之後,會解析出對象對應的類的生產方法和訂閱者法,訂閱者解析的結果儲存在handlersByType,生產者解析的結果儲存在producersByType裡,這兩個屬性定義如下
private final ConcurrentMap<Class<?>, Set<EventHandler>> handlersByType = new ConcurrentHashMap<Class<?>, Set<EventHandler>>(); /** All registered event producers, index by event type. */ private final ConcurrentMap<Class<?>, EventProducer> producersByType = new ConcurrentHashMap<Class<?>, EventProducer>();
handlersByType的key中儲存了訂閱者法的入參 參數類型,vaue中儲存著訂閱者具體的對象和對應方法producersByType的key中儲存了生產方法的返回值參數類型,vaue中儲存著生產者具體的對象和對應方法從源碼中我們可以發現,
在解析訂閱者之後,如果有對應的生產者,會自動調用生產方法,並自動調用一次訂閱者方法。
所謂有對應生產者,就是有Produce註解的方法返回值參數類型和有Subscribe註解的方法參數相同。這個過程涉及到以下三個方法public void register(Object object) private void dispatchProducerResultToHandler(EventHandler handler, EventProducer producer)
protected void dispatch(Object event, EventHandler wrapper)
(3)訊息發送
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()); 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); } } } if (!dispatched && !(event instanceof DeadEvent)) { post(new DeadEvent(this, event)); } dispatchQueuedEvents(); }這裡主要涉及兩個屬性
private final ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>> eventsToDispatch = new ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>>() { @Override protected ConcurrentLinkedQueue<EventWithHandler> initialValue() { return new ConcurrentLinkedQueue<EventWithHandler>(); } }; /** True if the current thread is currently dispatching an event. */ private final ThreadLocal<Boolean> isDispatching = new ThreadLocal<Boolean>() { @Override protected Boolean initialValue() { return false; } };當調用 public void post(Object event)這個方法之後,首先進行線程校正,然後解析出對應的訂閱者,如果有訂閱者,將event放入隊列中, 如果沒有,就作為一個DeadEvent,對於DeadEvent注釋是這樣說的 * Wraps an event that was posted, but which had no subscribers and thus could not be delivered.
* <p>Subscribing a DeadEvent handler is useful for debugging or logging, as it can detect misconfigurations in a
* system‘s event distribution.已經很明確了,就不再贅述。Bus在分發訊息之後迴圈從訊息佇列中取值,這跟android的handler訊息機制很像,不過bus中的迴圈在訊息取完之後就結束了。
protected void dispatchQueuedEvents() { // don't dispatch if we're already dispatching, that would allow reentrancy and out-of-order events. Instead, leave // the events to be dispatched after the in-progress dispatch is complete. if (isDispatching.get()) { return; } isDispatching.set(true); try { while (true) { EventWithHandler eventWithHandler = eventsToDispatch.get().poll(); if (eventWithHandler == null) { break; } if (eventWithHandler.handler.isValid()) { dispatch(eventWithHandler.event, eventWithHandler.handler); } } } finally { isDispatching.set(false); } }訊息佇列使用ThreadLocal保證了隊列的獨立性。同時多個線程會建立多個迴圈,提高了效率。發送的訊息很快就就可以分發。(4)解除綁定操作
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); 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(); }跟register類似,首先是對象非空校正,然後是線程校正,然後解除綁定,註冊跟解除綁定一定要成對,
沒有註冊不可以解除綁定,解除綁定之後不可以直接再次解除綁定。解除綁定主要是清理工作,減少不必要的記憶體,防止記憶體流失。解除綁定之後就不能再收到綁定對象相關的訊息了。3、AnnotatedHandlerFinder對註解的解析,構建生產者和訂閱者解析出來的資訊存放在PRODUCERS_CACHE,SUBSCRIBERS_CACHE中,
/** Cache event bus producer methods for each class. */ private static final ConcurrentMap<Class<?>, Map<Class<?>, Method>> PRODUCERS_CACHE = new ConcurrentHashMap<Class<?>, Map<Class<?>, Method>>(); /** Cache event bus subscriber methods for each class. */ private static final ConcurrentMap<Class<?>, Map<Class<?>, Set<Method>>> SUBSCRIBERS_CACHE = new ConcurrentHashMap<Class<?>, Map<Class<?>, Set<Method>>>();
結構相同,key值是解析對象對應的class,value是方法參數類型(生產者是返回資料類型,訂閱者是方法參數的資料類型)和對應方法。解析生產者依次調用:static Map<Class<?>, EventProducer> findAllProducers(Object listener),
private static void loadAnnotatedProducerMethods(Class<?> listenerClass,Map<Class<?>, Method> producerMethods) ,
private static void loadAnnotatedMethods(Class<?> listenerClass,Map<Class<?>, Method> producerMethods, Map<Class<?>, Set<Method>> subscriberMethods)。
解析訂閱者依次調用:static Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener) ,
private static void loadAnnotatedSubscriberMethods(Class<?> listenerClass,Map<Class<?>, Set<Method>> subscriberMethods) ,
private static void loadAnnotatedMethods(Class<?> listenerClassMap<Class<?>, Method> producerMethods, Map<Class<?>, Set<Method>> subscriberMethods)。
最終都是調用loadAnnotatedMethods在解析過程中,違反一些規則會拋出異常
@Subscribe註解的方法只能有一個參數
@Subscribe註解的方法必須有具體實現,不能是個介面
@Subscribe註解的方法必須是public的
@Produce註解的方法入參必須是空的
@Produce註解的方法返回值類型不能是void
@Produce註解的方法必須是具體實現,不能是個介面
@Produce註解的方法必須是public的
@Produce註解的方法相同返回值類型的方法只能有一個
四、總結OTTO是非常輕量級的,多數實現依賴註解反射,使用過程中如果要對代碼進行混淆要特別注意。android事件匯流排處理還有個很好的開源架構是EventBus,EventBus稍微重量級一些,複雜一些,對應的功能更多,對線程式控制制更加靈活。對EventBus的詳細介紹可以參照http://blog.csdn.net/robertcpp/article/details/51546714。用RXJAVA也可以實現事件匯流排,以後在做詳細說明。無論用哪個架構,歸根到底都是一種觀察者模式。可以根據項目需要選擇合適的架構。當然,通過原生的廣播,Handler機制,肯定也能實現。
歡迎掃描二維碼,關注公眾帳號
Android 事件匯流排OTTO使用說明和源碼解析