EventBus 源码试读(三)

转载请标明出处:【Gypsophila 的博客】 http://blog.csdn.net/astro_gypsophila/article/details/75330316

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 源码试读(三)_第1张图片

我觉得从流程图中可以看出,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事件总线分发库

你可能感兴趣的:(源码阅读)