您的位置:首页 > 移动开发 > Android开发

EventBus 源码试读(三)

2017-07-18 20:31 357 查看
转载请标明出处:【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<Class<?>> 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<Subscription> 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<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, 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<SubscriberInfoIndex>
索引集合的操作,最终会赋值到
SubscriberMethodFinder
类内的
private List<SubscriberInfoIndex> subscriberInfoIndexes;
。而这个类就是我们当初 register 操作中查找订阅者中响应函数的关键实现。

// 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.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 跟广播的区别?

二者类似。

在Android中广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器)。

广播:在一个地方注册广播,在另一个地方针对action发送广播、传递数据,对应注册的地方就可以收到广播。

EventBus:基于订阅/发布的模式。在需要接收数据的地方,进行注册与订阅,在需要发布数据的地方发布,则在注册的地方就可以收到了。

简单点说,就是两人约定好怎么通信,一人发布消息,另外一个约定好的人立马接收到你发的消息。EventBus就可以帮助减少做很多事,不管你在任何地方任何位置发布一个事件,接收者都能立马接收到你的消息,不用你考虑android子线程操作UI线程的问题。

二者区别。

(1)EventBus 有三个主要的元素:事件、订阅和发布。广播两个元素:订阅和发布,但是广播是针对整个App而言的。

(2)BroadcastReceiver是组件,需要在功能清单中注册。而EventBus 不需要注册。

(3)BroadcastReceiver只能做一件事情,而EventBus多事件处理比较好。

(4)在不同场景中的适用性:

1)同一App内部的同一组件内的消息通信(单个或多个线程之间),实际应用中肯定是不会用到广播机制的(虽然可以用),无论是使用扩展变量作用域、基于接口的回调还是Handler-post/Handler-Message等方式,都可以直接处理此类问题,若使用广播机制,显然有些“杀鸡牛刀”的感觉。

2)同一app内部的不同组件之间的消息通信(单个进程),对于此类需求,在有些教复杂的情况下单纯的依靠基于接口的回调等方式不好处理,此时可以直接使用EventBus等,相对而言,EventBus由于是针对同一进程,用于处理此类需求非常适合,且轻松。

3)其他情形,由于涉及不同进程间的消息通信,此时根据实际业务使用广播机制会显得非常适宜。

以上引用不敢做修改,我们只需要记住他们二者使用的简便性和不同场景的适用即可。

参考:

【攻克Android (35)】EventBus事件总线分发库
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息