当一个Android应用功能越来越多的时候,app中各个部分之间通信,往往采用Observer的方式来进行,即注册----通知----注销的方式执行 各类控件经常需要根据某个状态来更新显示内容。这种场景常见的解决方式就是定义一个接口,需要关注该事件的控件来实现这个接口。 接口类:
public interface OnChangedListener {
void onDataChanged();
}
被观察者往往以如下形式实现:
public abstract class AbsHTTPRequest { private final WeakHashMap<OnChangedListener, Boolean> mListeners = new WeakHashMap<OnChangedListener, Boolean>(); public interface OnChangedListener { void onDataChanged(); } /*HTTP's response*/ public abstract void onResponse(); public final void addListener(OnChangedListener listener) { mListeners.put(listener, true); } public final void removeListener(OnChangedListener listener) { mListeners.remove(listener); } protected final void notifyDataChanged() { Set<OnChangedListener> keys = mListeners.keySet(); if(keys != null) { Iterator<OnChangedListener> iterator = keys.iterator(); while(iterator.hasNext()) { iterator.next().onDataChanged(); } } } }
具体的主题角色( 被观察者),实现方式如下:
public class LoginRequest extends AbsHTTPRequest implements OnChangedListener{ public void onResponse(){ addListener(this); notifyDataChanged(); } @Override public void onDataChanged() { // TODO Auto-generated method stub System.out.println("LoginRequest"); } }
使用观察者模式有一个弊病就是部件之间的耦合度太高,所有的主题角色都需要实现同一个interface。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。如果主题角色被注册的observer越多,那么需要实现的interface也就越多,接口方法数量也就越多。
如何来进行解耦,让代码逻辑更清晰,可读性更强,是一个问题。
在Android中也有一个类似功能的开源库EventBus,可以很方便的帮助我们实现观察者模式,并且让各个组件之间的耦合性更低。
关于EventBus的讲解文章,网络业很多,这里推荐一篇,讲解比较详细的blog,http://www.cnblogs.com/angeldevil/p/3715934.html
对EventBus的认识,最好还是从demo入手,先易后难。首先知道如何使用,然后再深究源码,才能循序渐进,吃透其中的设计理念,便于日后的代码调试和模块重构。关于demo,网上有很多,可以自己去查收。
EventBus的使用有4个步奏:
1.定义事件类型:
public class MyEvent
2.注册订阅者:
EventBus.getDefault().register(this)
3.发送事件:
EventBus.getDefault().post(new MyEvent())
4.接收事件,处理
订阅者回调的函数。官方指导,函数名称以onEvent开头。
在EventBus模块中,有几个重要的概念,了解了这几个概念后,也就不难懂了。
Event:可以是任意类型的对象
Subscriber:订阅者,接收特定的
Publisher:发布者,用于通知Subscriber发送
EventType:onEvent函数中的参数,表示事件对象,用户自定义的。
Subscriber:订阅源,即调用register注册的对象,这个对象内包含onEvent成员函数。
SubscribMethod.java final class SubscriberMethod { final Method method; /*Method类型的method成员表示这个onEvent,即事件处理函数。同时也包含订阅源*/ final ThreadMode threadMode; final Class<?> eventType; /*事件的对象,用户自定义Object*/ ... ... ... ... ... ... ... ... ... ... ... ... }
Subscription.java
final class Subscription { final Object subscriber; /*订阅源Subscriber,即调用register注册的对象*/ final SubscriberMethod subscriberMethod; /**/ final int priority; ... ... ... ... ... ... ... ... ... ... ... ... }
类EventBus中,有两个核心的成员
/*EventType -> List<Subscription>,事件到订阅对象之间的映射*/ private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; /* Subscriber -> List<EventType>,订阅对象到它订阅的的所有事件的映射关系*/ private final Map<Object, List<Class<?>>> typesBySubscriber;
注册流程:在调用register函数时,EventBus类有多个重载的register函数,但是作者更倾向于使用register(this);含有 多个参数的register函数中,明确标注了@deprecated,原创作者不建议使用。从代码:
public void register(Object subscriber) { register(subscriber, DEFAULT_METHOD_NAME, false, 0); }
可以观察到,所有重载的register函数,都调用到了
private synchronized void register(Object subscriber, String methodName, boolean sticky, int priority) { List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass(),methodName); for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod, sticky, priority); } }
其中注册函数register,默认参数DEFAULT_METHOD_NAME为函数名称"onEvent",在java放射机制中,所有的事件处理函数名称 统一为“onEvent”,仅仅参数不一致。onEvent的参数为用户自定义的对象。
注册时,使用SubscriberMethodFinder的对象,调用到findSubscriberMethods方法,获取到List<SubscriberMethod>。
数组对象Method[],调用getMethods()方法, 获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。这也是为啥,我们的onEvent函数,要定义为public方法的原因哦。
在findSubscriberMethods函数中,进行如此频繁的遍历,就是为了找到List<SubscriberMethod>。 每一个订阅者,对应一个List<SubscriberMethod>,有多少onEvent函数,返回的List,就有多少个item。即查找订阅源内的事件处理方法,同时还会查到它的父类中的事件处理方法,返回list,交给
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)
进行处理。
Event与Subscriber之间,是一对多的关系。即一个事件,可以被多个订阅者关注。
Subscriber与Event之间,也是一对多的关系。即一个订阅者,可以订阅多个事件。
subscribe方法,也就是将上述的那样的关系,进行理顺,合理的建立map的映射关系,主要做了这样几件事件。
a.根据SubscriberMethod中的EventType类型,将Subscribtion对象存放在subscriptionsByEventType中。建立EventType与Subscription的映射,一个事件可以有多个订阅者。
b.根据Subscriber将EventType存放在typesBySubscriber`中,建立Subscriber到EventType的映射,每个Subscriber可以订阅多个事件。
c.如果是Sticky类型的订阅者,直接向它发送上个保存的事件(如果有的话)。
通过Subscriber到EventType的映射,我们就可以很方便地使一个Subscriber取消接收事件,通过EventType到Sucscribtion的映射,可以方便地将相应的事件发送到它的每一个订阅者。
与Observer不同的是,使用EventBus,不同的被观察者,不需统一实现Observer中的interface方法,在上层代码中,也不需要逐一进行notify机制。通过Map进行订阅源与事件函数的对应关系,进行解耦,为其核心之处。
发送流程:
EventBus.getDefault().post(new EventType());参数为用户自定义的对象。最为简单的处理方式,实现事件发送。
当事件发送出去后,所有的订阅者,是如何调用其事件方法的呢?这个就需要遍历上文提到的subscriptionsByEventType的Map了。Post发送事件,入口为post函数:public void post(Object event),在postSingleEvent函数中个,有一个重要的处理函数:
/** Finds all Class objects including super classes and interfaces. */ private List<Class<?>> findEventTypes(Class<?> eventClass) { synchronized (eventTypesCache) { List<Class<?>> eventTypes = eventTypesCache.get(eventClass); if (eventTypes == null) { eventTypes = new ArrayList<Class<?>>(); Class<?> clazz = eventClass; while (clazz != null) { eventTypes.add(clazz); addInterfaces(eventTypes, clazz.getInterfaces()); clazz = clazz.getSuperclass(); } eventTypesCache.put(eventClass, eventTypes); } return eventTypes; } }
其作用,就是把这个事件类的对象、实现的接口及父类的类对象存到一个List中返回,根据list中的eventTypes,遍历subscriptionsByEventType,获取订阅源对象,进行逐一的调用事件函数。
这里需要注意的是,当Post一个事件时,这个事件的父事件(事件类的父类事件)、接口事件也会被Post,所以如果订阅者接收Object类型的事件,即包含onEvent(Object object)事件函数,那么Subscriber就可以接收所有的事件。
通过本篇博文的了解,EventBus就是通过Map,建立订阅源与事件函数的对应关系,进行解耦,来规避Observer的接口方法的多次、频繁的定义。