本文针对EventBus version 3.1.1进行解读
前言
框架方便了我们的开发,加速了产品研发的效率。有时候我们可能只会用框架,但是遇到问题的时候却束手无策。古人云,授之以鱼不如授之以渔,这个时候只是差了一个源码解析。只有深入之中,才得其法。
我们不重复造轮子不表示我们不需要知道轮子该怎么造及如何更好的造!
本文集将持续更新Android主流第三方框架的原理解析,敬请期待。
简介
由greenrobot组织贡献(该组织还贡献了greenDAO),一个Android事件发布/订阅轻量级框架。
EventBus可以代替Android传统的Intent,Handler,Broadcast或接口函数,在Fragment,Activity,Service线程之间传递数据,执行方法。
功能:通过解耦发布者和订阅者简化Android事件传递。
看下图:
![EventBus3.x源码解析_第1张图片](http://img.e-com-net.com/image/info8/0bf2d7f5488d481d93b5c3ffab626f49.jpg)
有些文章会把发布-订阅模式与观察者模式混淆,其实它们之间还是有很大区别的。有兴趣的朋友可以查看这篇文章观察者模式和“发布-订阅”模式有区别吗?。
本文将从以下三个主要的主题来讲解:
1. EventBus创建
2. 注册为订阅者
3. 消息发送
4. 取消订阅
EventBus创建
先看代码:
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
一般我们会调用EventBus#getDefault(),懒汉模式(思考一下什么是饿汉模式)的单例模式,对于每一个不同的使用者来说,全局只有一个EventBus实例对象。
同时,我们还可以通过new EventBus()
的方式建立EventBus对象。但是一般不建议这么做。我们本来就是为了减低耦合度才使用的EventBus,如果每一个使用者采用这种方式,那么就会产生依赖注入、收发消息局限性等问题。当然,我们也可以构建自己的唯一EventBus对象,但是放在哪里好呢,肯定不能放在Activity这种有生命周期局限的组件内,那么也只能放在Application的初始化当中。
注册为订阅者
一般我们会采用以下方式在Activity中进行注册
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
EventBus.getDefault().register(this);
...
}
再抽丝源码之前我们先看看EventBus中几个重要的变量,有助于我们接下来的分析
private static final Map, List>> eventTypesCache = new HashMap<>();
private final Map, CopyOnWriteArrayList> subscriptionsByEventType;
private final Map
register()
public void register(Object subscriber) {
Class subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
register()方法很简单,首先通过findSubscriberMethods()查找订阅的方法集合,再通过遍历对订阅的方法进行一一注册。
findSubscriberMethods()订阅方法的查找流程
List findSubscriberMethods(Class subscriberClass) {
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
findSubscriberMethods()方法首先会从Map缓存中获取订阅方法集合。如果存在缓存,则直接返回。否则,会判断是否忽略ignoreGeneratedIndex。
插播一幅图:
![EventBus3.x源码解析_第2张图片](http://img.e-com-net.com/image/info8/c060aab43d1741ef87ca9c2ca477ac05.png)
Subscriber Index技术使得订阅者注册速度快了不只是一点点。原因很简单,通过编译时生成订阅者订阅方法的一些信息,这样就避免了运行时通过反射进行查找而造成的性能问题。Subscriber Index技术使用很简单,可以参见官方介绍Subscriber Index
ignoreGeneratedIndex的默认值为false,我们也可以通过EventBusBuilder重新初始化。
当ignoreGeneratedIndex为true时会通过findUsingReflection()进行查找,否则会通过findUsingInfo()进行查找。如果没有没有订阅方法,则抛出异常。如果有订阅方法,则将订阅方法加入到缓存当中。
findUsingInfo()
private List findUsingInfo(Class subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
在FindState里面,它保存了一些订阅者的方法以及对订阅方法的校验。通过initForSubscriber初始化了FindState的clazz属性。于是下面便进入while循环当中。通过getSubscriberInfo方法来获取订阅者信息。
在我们开始查找订阅方法的时候并没有忽略注解器为我们生成的索引MyEventBusIndex,如果我们通过EventBusBuilder配置了MyEventBusIndex,便会获取到subscriberInfo。而在MyEventBusIndex中会将订阅方法相关的信息存放在SubscriberMethodInfo类中,这个时候就不在需要通过注解进行获取订阅方法。
如果没有配置MyEventBusIndex,便会执行findUsingReflectionInSingleClass方法,将订阅方法保存到findState中。最后再通过getMethodsAndRelease方法对findState做回收处理并反回订阅方法的List集合。
对于MyEventBusIndex的配置它是一个可选项。它的执行过程在这里就不在进行详细的介绍,下面就来看一下如何通过反射来查找订阅方法,也就是看一下findUsingReflectionInSingleClass的执行过程。
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods
//获取Method
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
methods = findState.clazz.getDeclaredMethods()
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
methods = findState.clazz.getMethods()
findState.skipSuperClasses = true
}
for (Method method : methods) {
int modifiers = method.getModifiers()
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class[] parameterTypes = method.getParameterTypes()
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class)
if (subscribeAnnotation != null) {
Class eventType = parameterTypes[0]
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode()
//保存到findState对象当中
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()))
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName()
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length)
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName()
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract")
}
}
}
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
List> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) {
if (eventInheritance) {
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, Object> entry : entries) {
Class candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
消息发送
现在来看一下事件的发送过程。在获取到EventBus对象以后,通过post方法来进行对事件的提交,下面就来看一下整个post方法是如何对事件进行提交的。
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List