EventBus 源码试读(三)

转载请标明出处:【Gypsophila 的博客】 http://blog.csdn.net/astro_gypsophila/article/details/75330316
【EventBus 源码试读(三)】EventBus 基本使用和进阶配置
EventBus 源码试读(一)
EventBus 源码试读(二)
EventBus 源码试读(三)
源码试读之 unregister

// EventBus 类中 /** Unregisters the given subscriber from all event classes. */ public synchronized void unregister(Object subscriber) { // 获得该订阅者所订阅的事件集合 List> subscribedTypes = typesBySubscriber.get(subscriber); if (subscribedTypes != null) { // 循环解除该订阅的者的每一个事件对应的响应函数 for (Class eventType : subscribedTypes) { unsubscribeByEventType(subscriber, eventType); } typesBySubscriber.remove(subscriber); } else { Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass()); } }// EventBus 类中 /** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */ private void unsubscribeByEventType(Object subscriber, Class eventType) { // 根据事件来获取 Subscription 集合, Subscription 是订阅者类和响应方法的包装 List subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions != null) { int size = subscriptions.size(); // 从 Subscription 集合中移除该订阅者,同一个订阅者下可能拥有多个 Subscription 对象 for (int i = 0; i < size; i++) { Subscription subscription = subscriptions.get(i); if (subscription.subscriber == subscriber) { subscription.active = false; subscriptions.remove(i); i--; size--; } } } }

unregister 的逻辑比较简单,是将 register 时所记录的一些集合中移除该订阅者的一些信息。这样当 post 时候循环发布消息时就不会收到了。
另外 unsubscribeByEventType() 中的 for 循环,由于订阅者下是可以存在多个订阅同一个事件的响应函数,所以要 for 循环完全遍历,而不是一找到后就 break。
源码试读之订阅者索引 关于这部分来说,在 EventBus 基本使用和进阶配置 已经提到过它的使用和作用,Subscriber index 在编译期间通过 EventBus annotation processor(注解处理器) 被创建起来的,这个可以更快速度获得订阅者,从而去触发响应事件。
我们仍然依照 EventBus 基本使用和进阶配置 中用到的演示界面为 ActivityFirst–>ActivitySecond–>ActivityThird 的示例。
关于 MyEventBusIndex 的全限定名是可以在 build.gradle 自由配置,但它整体是在 app/build/generated/source/apt/debug/ 路径下的。
build.gradle 配置
android { ... defaultConfig { ... javaCompileOptions { annotationProcessorOptions { arguments = [ eventBusIndex : 'com.example.myapp.MyEventBusIndex' ] } } } ... }

配置好 build.gradle,真正使它生效,仅仅添加以下代码就好:
EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build(); // 或者 EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus(); // Now the default instance uses the given index. Use it like this: EventBus eventBus = EventBus.getDefault();

MyEventBusIndex 内容
/** This class is generated by EventBus, do not edit. */ public class MyEventBusIndex implements SubscriberInfoIndex { private static final Map, SubscriberInfo> SUBSCRIBER_INDEX; static { SUBSCRIBER_INDEX = new HashMap, SubscriberInfo>(); putIndex(new SimpleSubscriberInfo(com.gypsophila.eventbustest.ActivityThird.class, true, new SubscriberMethodInfo[] { new SubscriberMethodInfo("onStickyEvent", com.gypsophila.eventbustest.events.MessageEvent.class, ThreadMode.MAIN, 0, true), new SubscriberMethodInfo("onMessageEvent", com.gypsophila.eventbustest.events.MessageEvent.class, ThreadMode.MAIN), })); putIndex(new SimpleSubscriberInfo(com.gypsophila.eventbustest.ActivityFirst.class, true, new SubscriberMethodInfo[] { new SubscriberMethodInfo("onMessageEvent1", com.gypsophila.eventbustest.events.MessageEvent.class, ThreadMode.POSTING, 1, false), new SubscriberMethodInfo("onMessageEvent2", com.gypsophila.eventbustest.events.MessageEvent.class, ThreadMode.POSTING, 2, false), new SubscriberMethodInfo("onMessageEvent3", com.gypsophila.eventbustest.events.MessageEvent.class), })); }private static void putIndex(SubscriberInfo info) { SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info); }@Override public SubscriberInfo getSubscriberInfo(Class subscriberClass) { SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass); if (info != null) { return info; } else { return null; } } }

以上记录了所有订阅类中订阅的事件对应的响应函数。由于在编译期就建立起了这样的索引,这样的一个订阅者类与其类中的响应函数的对应关系,使我们可以不必在运行期通过反射获取方法,可以算是一个优化。
// 从 addIndex 入手 EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();

这边的 addIndex()添加索引类所生成的 List 索引集合的操作,最终会赋值到 SubscriberMethodFinder 类内的 private List subscriberInfoIndexes; 。而这个类就是我们当初 register 操作中查找订阅者中响应函数的关键实现。
// 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.moveToSuperclass(); } return getMethodsAndRelease(findState); }// SubscriberMethodFinder 类中 private SubscriberInfo getSubscriberInfo(FindState findState) { if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) { SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo(); if (findState.clazz == superclassInfo.getSubscriberClass()) { return superclassInfo; } } // 如果有索引集合 if (subscriberInfoIndexes != null) { // 从每个索引类中查找订阅者的类类型,得到 SubscriberInfo 对象 // SubscriberInfo 包装了指定订阅者内的所有响应函数 for (SubscriberInfoIndex index : subscriberInfoIndexes) { SubscriberInfo info = index.getSubscriberInfo(findState.clazz); if (info != null) { return info; } } } return null; }

关于一次全新的查找,如下图的流程,但之后如果继续注册了相同的订阅者,就是从缓存中直接取出。
EventBus 源码试读(三)
文章图片

我觉得从流程图中可以看出,EventBus 通过不同方式,尽量的去提升性能。所以并不会因为使用反射出现很大的性能问题,我们更应该关注它的易用性,项目中使用它还是非常简便的。
到这里,EventBus 主要流程也看的差不多了。但是关于其他的细节还是很值得去看的,毕竟 EventBus 这个库的类不多,正适合慢慢看,慢慢学习,而避免了大库的深陷细节找不到北的烦恼。
这边我们思考一个问题:EventBus 跟广播的区别?
  1. 二者类似。
    在Android中广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器)。
    广播:在一个地方注册广播,在另一个地方针对action发送广播、传递数据,对应注册的地方就可以收到广播。
    EventBus:基于订阅/发布的模式。在需要接收数据的地方,进行注册与订阅,在需要发布数据的地方发布,则在注册的地方就可以收到了。
    简单点说,就是两人约定好怎么通信,一人发布消息,另外一个约定好的人立马接收到你发的消息。EventBus就可以帮助减少做很多事,不管你在任何地方任何位置发布一个事件,接收者都能立马接收到你的消息,不用你考虑android子线程操作UI线程的问题。
  2. 二者区别。
    (1)EventBus 有三个主要的元素:事件、订阅和发布。广播两个元素:订阅和发布,但是广播是针对整个App而言的。
    (2)BroadcastReceiver是组件,需要在功能清单中注册。而EventBus 不需要注册。
    (3)BroadcastReceiver只能做一件事情,而EventBus多事件处理比较好。
    (4)在不同场景中的适用性:
    1)同一App内部的同一组件内的消息通信(单个或多个线程之间),实际应用中肯定是不会用到广播机制的(虽然可以用),无论是使用扩展变量作用域、基于接口的回调还是Handler-post/Handler-Message等方式,都可以直接处理此类问题,若使用广播机制,显然有些“杀鸡牛刀”的感觉。
    2)同一app内部的不同组件之间的消息通信(单个进程),对于此类需求,在有些教复杂的情况下单纯的依靠基于接口的回调等方式不好处理,此时可以直接使用EventBus等,相对而言,EventBus由于是针对同一进程,用于处理此类需求非常适合,且轻松。
    3)其他情形,由于涉及不同进程间的消息通信,此时根据实际业务使用广播机制会显得非常适宜。
以上引用不敢做修改,我们只需要记住他们二者使用的简便性和不同场景的适用即可。
参考:
【攻克Android (35)】EventBus事件总线分发库

    推荐阅读