转载请标明出处:【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 跟广播的区别?
以上引用不敢做修改,我们只需要记住他们二者使用的简便性和不同场景的适用即可。
- 二者类似。
在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事件总线分发库