編輯:關於Android編程
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庫可以。
public class AppConfig { private static final Bus BUS = new Bus(); public static Bus getInstance() { return BUS; } }
在類創建好之後,或者需要重新注冊的時候注冊,一般在Activity的onCreate()或者onPause()方法中
AppConfig.getBusInstance().register(this);
@Subscribe public void onWallpaperUpdate(MyObject obj) { //對obj進行需要的邏輯處理 }
AppConfig.getBusInstance().post(myobj);
注意在類銷毀的時候或者暫時不需要再收消息的時候解綁,,一般在Activity的onDestroy()或者onResume()方法中
AppConfig.getBusInstance().unregister(this);
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,使用注解解析
public void register(Object object) { if (object == null) { throw new NullPointerException("Object to register must not be null."); } enforcer.enforce(this); Map對象注冊首先進行了非空校驗,然後是線程的校驗,對象不可為空,不可多次注冊 在注冊對象之後,會解析出對象對應的類的生產方法和訂閱方法,訂閱者解析的結果保存在handlersByType,生產者解析的結果保存在producersByType裡,這兩個屬性定義如下, 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 handlers = handlersByType.get(type); if (handlers != null && !handlers.isEmpty()) { for (EventHandler handler : handlers) { dispatchProducerResultToHandler(handler, producer); } } } Map , Set > foundHandlersMap = handlerFinder.findAllSubscribers(object); for (Class type : foundHandlersMap.keySet()) { Set handlers = handlersByType.get(type); if (handlers == null) { //concurrent put if absent Set handlersCreation = new CopyOnWriteArraySet (); handlers = handlersByType.putIfAbsent(type, handlersCreation); if (handlers == null) { handlers = handlersCreation; } } final Set foundHandlers = foundHandlersMap.get(type); if (!handlers.addAll(foundHandlers)) { throw new IllegalArgumentException("Object already registered."); } } for (Map.Entry , Set > entry : foundHandlersMap.entrySet()) { Class type = entry.getKey(); EventProducer producer = producersByType.get(type); if (producer != null && producer.isValid()) { Set foundHandlers = entry.getValue(); for (EventHandler foundHandler : foundHandlers) { if (!producer.isValid()) { break; } if (foundHandler.isValid()) { dispatchProducerResultToHandler(foundHandler, producer); } } } } }
private final ConcurrentMaphandlersByType的key中保存了訂閱方法的入參 參數類型,vaue中保存著訂閱者具體的對象和對應方法 producersByType的key中保存了生產方法的返回值參數類型,vaue中保存著生產者具體的對象和對應方法 從源碼中我們可以發現,在解析訂閱者之後,如果有對應的生產者,會自動調用生產方法,並自動調用一次訂閱者方法。, Set > handlersByType = new ConcurrentHashMap , Set >(); /** All registered event producers, index by event type. */ private final ConcurrentMap , EventProducer> producersByType = new ConcurrentHashMap , EventProducer>();
public void post(Object event) { if (event == null) { throw new NullPointerException("Event to post must not be null."); } enforcer.enforce(this); Set這裡主要涉及兩個屬性> dispatchTypes = flattenHierarchy(event.getClass()); boolean dispatched = false; for (Class eventType : dispatchTypes) { Set 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當調用 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.> eventsToDispatch = new ThreadLocal >() { @Override protected ConcurrentLinkedQueue initialValue() { return new ConcurrentLinkedQueue (); } }; /** True if the current thread is currently dispatching an event. */ private final ThreadLocal isDispatching = new ThreadLocal () { @Override protected Boolean initialValue() { return false; } };
Subscribing a DeadEvent handler is useful for debugging or logging, as it can detect misconfigurations in a
* system's event distribution.
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保證了隊列的獨立性。同時多個線程會創建多個循環,提高了效率。發送的消息很快就就可以分發。
public void unregister(Object object) { if (object == null) { throw new NullPointerException("Object to unregister must not be null."); } enforcer.enforce(this); Map跟register類似,首先是對象非空校驗,然後是線程校驗,然後解綁,注冊跟解綁一定要成對,沒有注冊不可以解綁,解綁之後不可以直接再次解綁。 解綁主要是清理工作,減少不必要的內存,防止內存洩漏。解綁之後就不能再收到綁定對象相關的消息了。, EventProducer> producersInListener = handlerFinder.findAllProducers(object); for (Map.Entry , 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(); }
/** Cache event bus producer methods for each class. */ private static final ConcurrentMap, Map , Method>> PRODUCERS_CACHE = new ConcurrentHashMap , Map , Method>>(); /** Cache event bus subscriber methods for each class. */ private static final ConcurrentMap , Map , Set >> SUBSCRIBERS_CACHE = new ConcurrentHashMap , Map , Set >>();
今天你被耍流氓了嗎?微信聊天時有沒有收到“XX撤回一條消息並親了你一下”的消息,在朋友圈中一排排“送你一朵小花&rdqu
現在的移動端應用幾乎都會通過網絡請求來和服務器交互,通過抓包來診斷和網絡相關的bug是程序員的重要技能之一。抓包的手段有很多:針對http和https可以使用Charle
文件main.java復制代碼 代碼如下:package com.HHBrowser.android;import android.app.Activity;import
關於自定義屬性,我們用的比較多的時候就是在自定義view的時候了,其實自定義屬性還有一些其余的妙用。這裡講解一個利用自定義的屬性為應用程序全局的替換背景的例子。1.And