转载请标明出处:【Gypsophila 的博客】 http://blog.csdn.net/astro_gypsophila/article/details/75269270
EventBus 基本使用和进阶配置
EventBus 源码试读(一)
EventBus 源码试读(二)
EventBus 源码试读(三)
EventBus 是 Android 平台上优化的发布/订阅事件总线库。
既然能看到这篇文章,说明已经了解过了 EventBus 的基本使用,如果还不了解的话,请参看 EventBus 基本使用和进阶配置。
而对于这篇文章来说,主要是尝试阅读 EventBus 的源码,了解其内部工作原理。
以下是 EventBus 主要类的关系图:
其中这图是参考自 EventBus 源码解析,但随着 EventBus 版本更新,出现了个别类的微小改变。另外我觉得关于 SubscriberMethodFinder 中的索引类加速查找 应该要讲一下,所以修改了点,还请忽略我绘图的不准确。
在阅读源码前,我们需要思考,对待一份源码从哪里看起来比较好。如果无从下手,那就从你经常调用的地方进行切入,这虽然不是程序的起始点,但相对而言你会比较熟悉切入点接下来的流程,并且可以通过想象脑补出切入点之前可能做了哪些操作。
我们最通常的用法,就是在 EventBus 基本使用和进阶配置 中的介绍那样,三步使用 EventBus,即 register()
、unregister()
、post()
。
// EventBus 类中
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()
看下到底如何获取到这些响应函数的,然后再回头来看,for 循环中的 subscribe(),猜想也不过是循环拿着每一个获取来的符合条件的响应函数做一些处理而已。
// SubscriberMethodFinder 类中
List findSubscriberMethods(Class> subscriberClass) {
// 从方法缓存 Map 中取得对应的方法集合,有的话就直接返回
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
// 是否忽略索引,默认为 false
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;
}
}
// SubscriberMethodFinder 类中
private List findUsingInfo(Class> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
// 显然如果 subscriberClass 传入非 null,则会进入 while 循环
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.clazz 为空退出循环
findState.moveToSuperclass();
}
// 返回查找到的响应函数集合
return getMethodsAndRelease(findState);
}
我们直接看 while 循环中的 40-42 行findUsingReflectionInSingleClass()
,其实程序一开始运行时,EventBus 一定是没有缓存那些响应函数的。这时候就是利用反射机制,去根据 register()
参数的类类型,去获取符合条件的响应函数,所以核心就是反射机制。
// SubscriberMethodFinder 类中
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
// 获取 clazz 对应的类中声明的方法
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 循环作用是将获取的 methods 数组按照各种条件过滤
for (Method method : methods) {
int modifiers = method.getModifiers();
// 响应函数至少是 public 修饰的,并且不能是被 static 和 abstract 修饰
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class>[] parameterTypes = method.getParameterTypes();
// 响应函数的参数应该有且只有1个,那就是对应的事件
if (parameterTypes.length == 1) {
// 响应函数必须是被 Subscribe 注解的
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
// 根据 Subscribe 上注解的各个值去构造一个 SubscriberMethod 对象
// 即 SubscriberMethod 包装了真正的响应函数和其他值
ThreadMode threadMode = subscribeAnnotation.threadMode();
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");
}
}
}
以上算是查找了订阅类的响应函数,现在回头看下 register()
中 for 循环内的 subscribe()
做了那些操作。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class> eventType = subscriberMethod.eventType;
// 根据订阅者类和订阅函数构造的一个 Subscription 包装对象
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// 根据事件类型,获取包装类的集合
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
// 一般而言,正确使用的话,subscriptions 都将是 null
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
// 说明可能重复 register 了
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
// 根据同一事件的响应函数的优先级顺序进行排序,放入 newSubscription 到合适位置
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);
// 如果这类事件是 sticky 事件,那么就直接调用响应函数
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List).
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);
}
}
}
subscribe()
我认为是将新 register 注册的订阅类中查找到所有的响应函数(存在订阅了不同事件类型),对其根据事件类型,进行分离,分别加入到按照事件类型分类的 Map 中的集合之中。它会在接下来的 post
动作中被使用起来。
另外需要注意的是,我们看到响应函数的注解 sticky 为 true,那么此时它就会马上被调用执行。
EventBus 源码解析