EventBus 源码试读(一)
来源:互联网 发布:sql exists in 效率 编辑:程序博客网 时间:2024/05/21 21:40
转载请标明出处:【Gypsophila 的博客】 http://blog.csdn.net/astro_gypsophila/article/details/75269270
EventBus 基本使用和进阶配置
EventBus 源码试读(一)
EventBus 源码试读(二)
EventBus 源码试读(三)
EventBus 概览
EventBus 是 Android 平台上优化的发布/订阅事件总线库。
既然能看到这篇文章,说明已经了解过了 EventBus 的基本使用,如果还不了解的话,请参看 EventBus 基本使用和进阶配置。
而对于这篇文章来说,主要是尝试阅读 EventBus 的源码,了解其内部工作原理。
以下是 EventBus 主要类的关系图:
其中这图是参考自 EventBus 源码解析,但随着 EventBus 版本更新,出现了个别类的微小改变。另外我觉得关于 SubscriberMethodFinder 中的索引类加速查找 应该要讲一下,所以修改了点,还请忽略我绘图的不准确。
源码试读之 register
在阅读源码前,我们需要思考,对待一份源码从哪里看起来比较好。如果无从下手,那就从你经常调用的地方进行切入,这虽然不是程序的起始点,但相对而言你会比较熟悉切入点接下来的流程,并且可以通过想象脑补出切入点之前可能做了哪些操作。
我们最通常的用法,就是在 EventBus 基本使用和进阶配置 中的介绍那样,三步使用 EventBus,即 register()
、unregister()
、post()
。
// EventBus 类中public void register(Object subscriber) { // 获取外部传入的订阅者的类类型(我没有卡带) Class<?> subscriberClass = subscriber.getClass(); // 获取对应订阅者类型的响应函数集合(这些方法其实都见名知意) List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } }}
在 register()
中,我们先直接深入 findSubscriberMethods()
看下到底如何获取到这些响应函数的,然后再回头来看,for 循环中的 subscribe(),猜想也不过是循环拿着每一个获取来的符合条件的响应函数做一些处理而已。
// SubscriberMethodFinder 类中List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { // 从方法缓存 Map 中取得对应的方法集合,有的话就直接返回 List<SubscriberMethod> 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<SubscriberMethod> 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<Subscription> 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<Class<?>> 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<Class>). Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet(); for (Map.Entry<Class<?>, 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 源码解析
- EventBus 源码试读(一)
- EventBus 源码试读(二)
- EventBus 源码试读(三)
- EventBus 源码解析(一)
- EventBus源码解析一
- EventBus源码分析(一):EventBus的使用
- Android EventBus实战及源码解析 (一)
- EventBus的源码解析(一)
- EventBus源码详解(一):基本使用
- zookeeper curator 源码试读
- Struts2源码试读1-filter
- EventBus(一)
- 读源码练内功(一):guava之eventbus
- Android EventBus 3.0 使用及源码解析一
- struts2源码试读——FilterChain、PrepareOperations
- EventBus 源码解析
- EventBus源码的理解
- EventBus 源码解析
- XML
- duilib 入门四之UIBase
- Linux文件前面加点例如.bashrc表示什么意思
- H5 CSS基础二:清除浮动是个难点
- vue-cli for循环
- EventBus 源码试读(一)
- Scala控制结构
- 文章标题
- 判断某一年是否是闰年
- linux device tree 详解
- HDU-4291 A Short problem(矩阵快速幂)
- 虚拟机的桥接模式,NAT,仅主机三种网络模式
- Hacker(一)
- spring单例模式与线程安全问题的解决方案