同步發表於http://avenwu.net/ioc/2015/01/29/custom_eventbusjava
Fork on github https://github.com/avenwu/supportgit
Android有廣播和Receiver能夠處理消息的傳遞和響應,要進行消息-發佈-訂閱,除此以外做爲開發者如今也有其餘相似的方案能夠選擇,好比EventBus和Otto,都是比較熱門的三方庫。那麼這些三方庫究竟是怎麼實現模塊之間的解耦,使得消息能夠再不一樣的系統組件之間傳遞呢?github
因爲是開源的,徹底能夠經過分析源代碼來了解這些個消息-發佈-訂閱方案在Android內是怎麼實現的,下面分別針對EventBus, Otto,Guava簡單分析。多線程
從github上獲取最新的EventBus代碼ide
git clone git@github.com:greenrobot/EventBus.git
直接找到EventBus這個類,從使用角度開始分析:工具
EventBus的使用能夠參考項目wiki,簡單的來講就是EventBus#register(),EventBus#post(),實現onEventXXX
Otto是大名鼎鼎的Square開發的oop
git clone git@github.com:square/otto.git
從代碼上來看EventBus和Otto很是像,不知道EventBus做者在設計編碼時是否參考了Otto得設計,Otto項目則明確表示其是基於Google的Guava而來,Guava是Google開發的一個工具類庫包含了很是多的實用工具類,其中就有一個EventBus模塊,可是這個EventBus是並無針對Android平臺作線程方面的考量。post
因此三者的是有關聯的:測試
因爲源代碼也很多,因此只列舉了核心代碼對應的位置,感興趣的童鞋確定會本身去研讀。this
上面的庫固然不是爲了研究而研究,如今理解了他們的核心思路後,咱們其實已經能夠着手本身寫一個簡單版的消息-發佈-訂閱。
如今先定義要實現的程度:
一個簡單的Bus大體上須要有幾個東西,Bus消息中心,負責綁定/解綁,發佈/訂閱;Finder查找定義好的消息處理方法;PostHandler分發消息並處理.
這裏的Bus作成單例,這樣不管在什麼地方註冊,發佈都是有這個消息中心來處理。
用一個Map來保存咱們的訂閱關係,當消息到達時從map中取出該消息類型的全部訂閱方法,經過反射依次invoke。
public class Bus { static volatile Bus sInstance; Finder mFinder; Map<Class<?>, CopyOnWriteArrayList<Subscriber>> mSubscriberMap; PostHandler mPostHandler; private Bus() { mFinder = new NameBasedFinder(); mSubscriberMap = new HashMap<>(); mPostHandler = new PostHandler(Looper.getMainLooper(), this); } public static Bus getDefault() { if (sInstance == null) { synchronized (Bus.class) { if (sInstance == null) { sInstance = new Bus(); } } } return sInstance; } public void register(Object subscriber) { List<Method> methods = mFinder.findSubscriber(subscriber.getClass()); if (methods == null || methods.size() < 1) { return; } CopyOnWriteArrayList<Subscriber> subscribers = mSubscriberMap.get(subscriber.getClass()); if (subscribers == null) { subscribers = new CopyOnWriteArrayList<>(); mSubscriberMap.put(methods.get(0).getParameterTypes()[0], subscribers); } for (Method method : methods) { Subscriber newSubscriber = new Subscriber(subscriber, method); subscribers.add(newSubscriber); } } public void unregister(Object subscriber) { CopyOnWriteArrayList<Subscriber> subscribers = mSubscriberMap.remove(subscriber.getClass()); if (subscribers != null) { for (Subscriber s : subscribers) { s.mMethod = null; s.mSubscriber = null; } } } public void post(Object event) { //TODO post with handler mPostHandler.enqueue(event); } }
查找訂閱方法便可以用註解,也能夠用命名約定,這裏先實現命名約定的方式。
爲了處理方便這裏和EventBus不徹底一致,只作了方法名和參數的限制,可是最好實現的嚴謹些。
public class NameBasedFinder implements Finder { @Override public List<Method> findSubscriber(Class<?> subscriber) { List<Method> methods = new ArrayList<>(); for (Method method : subscriber.getDeclaredMethods()) { if (method.getName().startsWith("onEvent") && method.getParameterTypes().length == 1) { methods.add(method); Log.d("findSubscriber", "add method:" + method.getName()); } } return methods; } }
分發消息確定要用到Handler,EventBus中本身維護了一個隊列來來處理消息的入棧、出棧,我這裏就世界用了Message來傳遞
public class PostHandler extends Handler { final Bus mBus; public PostHandler(Looper looper, Bus bus) { super(looper); mBus = bus; } @Override public void handleMessage(Message msg) { CopyOnWriteArrayList<Subscriber> subscribers = mBus.mSubscriberMap.get(msg.obj.getClass()); for (Subscriber subscriber : subscribers) { subscriber.mMethod.setAccessible(true); try { subscriber.mMethod.invoke(subscriber.mSubscriber, msg.obj); } catch (Exception e) { e.printStackTrace(); } } } void enqueue(Object event) { Message message = obtainMessage(); message.obj = event; sendMessage(message); } }
基本上的代碼都在這裏,實現一個Bus仍是挺簡單的,固然若是吧各類狀況都考慮進去就會變得複雜一些,好比支持多線程線程,也不可能想本文這樣區區數百行代碼就搞定。
感興趣的能夠到這裏獲取上面自定義bus的源代碼:https://github.com/avenwu/support/tree/master/support/src/main/java/net/avenwu/support