同步发表于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