面试有一种技巧据说叫做反客为主,当遇到Activity-Fragment通信,甚至模块化开发时的通信问题等等,可以将TA引导到你擅长的领域中来,比如说用EventBus来解决,(RxBus也可以)这时一般套路都会问你懂不懂EventBus的原理,这时你就可以以下文的姿势这样回(zhuang)答(b)了
(๑•̀ㅂ•́)و✧
简介
今天我们来分析截止到现在的3.0.0最新版本
惯例介绍一下我们今天的主角,EventBus,专门管理Android事件通讯。
//依赖
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
}
在Android开发中难免会遇到以下场景:
- Activity之间的回调
startActivityForResult
- 某个页面刷新后联动其他页面也刷新
- Fragment之间的通信
- more
按笔者之前的尿性就是复杂场景用本地广播做发送与监听,广播大吼一声,我叫你一声你敢答应吗(๑•̀ㅂ•́)و✧,是不是很有代入感,直到后来发现广播的性能与Intent传递数据大小有限制以及相对难以维护后来吃下了EventBus这发安利。
笔者先简单的写个基本用法
需要接收事件的类中注册监听(不局限Activity)
EventBus.getDefault().register(this);
别忘记在销毁时反注册
EventBus.getDefault().unregister(this);
发送事件
EventBus.getDefault().post(Object event);
在需要接收事件的类中随便写个方法,例如
//别忘了这个注解
@Subscribe
public void onEventReceive(String event) {
}
这个方法只要一个参数就够了,由于我们发送事件的时候EventBus根据类型来做校验,例如post("test"),此时所有注册的类中带有Subscribe注解的方法中只要第一个参数是String类型,那么就会调用这个方法,你问我怎么知道的,我等会再告诉你(__)
浅析
在EventBus光鲜的外表下我们好像看到了观察者模式的影子,没错,我们来从TA的入口开始EventBus.getDefault().register(this);
中挨个的调用顺序
public class EventBus {
//暂时省略一些代码
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
public EventBus() {
this(DEFAULT_BUILDER);
}
}
一个常见的DCL单例模式+构建者模式,我们再来看看EventBusBuilder
的构造器
public class EventBusBuilder {
//默认的线程池
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
//是否打印出订阅时抛出异常的日志
boolean logSubscriberExceptions = true;
//是否打印出没有订阅者异常的日志
boolean logNoSubscriberMessages = true;
//当订阅者订阅方法执行抛出异常时 ,是否让EventBus发送一个特定事件
boolean sendSubscriberExceptionEvent = true;
//当事件没有订阅者时,是否让EventBus发送一个特定事件
boolean sendNoSubscriberEvent = true;
//是否抛出订阅者的异常
boolean throwSubscriberException;
//是否允许事件可以有继承
boolean eventInheritance = true;
//是否忽视生成的索引
boolean ignoreGeneratedIndex;
//是否方法严格校验
boolean strictMethodVerification;
//线程池
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
//某些类跳过验证,类似绿色通道
List> skipMethodVerificationForClasses;
//订阅者的索引集合
List subscriberInfoIndexes;
EventBusBuilder() {
}
}
贴心的我又都给你们打上注释了,一下子看不明白没关系,后面我们还会再见面的
//EventBus的一参构造器
EventBus(EventBusBuilder builder) {
//根据消息类型的订阅者们,key对应消息的类型,value对应订阅这个类型消息的订阅者们
subscriptionsByEventType = new HashMap, CopyOnWriteArrayList>();
//根据订阅者的类型们,key对应订阅者,value对应在这个订阅者订阅的事件类型们
typesBySubscriber = new HashMap
如果看官稍微留意一点注释,相信已经对EventBus有所猜测了,没错,在register
方法中EventBus肯定对订阅者进行了不为人知的操作,将订阅者和接受的事件等等统统做了记录,抱着这样的猜测我们来瞧瞧register
public void register(Object subscriber) {
//获取class
Class> subscriberClass = subscriber.getClass();
//搜寻器去寻找订阅者要订阅的的集合
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//同步遍历后一一订阅
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
这里可以这样打个比方,subscriber
比作小明
,小明
想订报纸,他去找EventBus,说我要订报纸,然后EventBus派遣搜寻器小刚
去整理你要的类型,一看你原来要订旅游(String),拍照(Integer)和运动(Boolean),然后就去专门的专栏一一订阅,以后这些专栏一更新,EventBus就把报纸送到你的手上。
这里我们来看看subscriberMethodFinder
的初始化,初始化就在上文EventBus的构造器中
SubscriberMethodFinder(List subscriberInfoIndexes, boolean strictMethodVerification,
boolean ignoreGeneratedIndex) {
this.subscriberInfoIndexes = subscriberInfoIndexes;
this.strictMethodVerification = strictMethodVerification;
this.ignoreGeneratedIndex = ignoreGeneratedIndex;
}
将订阅者的索引集合,是否方法严格校验,是否忽视索引3个参数传入,有关索引做这块先卖个关子,先提前剧透一下,今天是小明
要订报纸,明天又来个小张
,小刚
每次拿笔都要记一下再去专栏订阅,小张
觉得太累了,于是想了个法子,每次要来订报纸,你们先填张表,填完给我,这样将就省事多了。
深入
接下来我们来看看搜寻器的寻找方法有没有什么猫腻
List findSubscriberMethods(Class> subscriberClass) {
//优先先从订阅者的缓存集合中寻找
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
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;
}
}
同理我们先跳过索引,先来分析findUsingReflection
方法
private List findUsingReflection(Class> subscriberClass) {
//保存记录
FindState findState = prepareFindState();
//初始化
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//通过反射来获取订阅方法
findUsingReflectionInSingleClass(findState);
//移动到父类
findState.moveToSuperclass();
}
//将最后结果返回并回收资源
return getMethodsAndRelease(findState);
}
FindState
就在SubscriberMethodFinder
下作为静态内部类,具体如下
static class FindState {
//寻找到的订阅方法
final List subscriberMethods = new ArrayList<>();
//所有符合事件类型的方法,包含父类的
//key对应事件类型,value对应方法Method
final Map anyMethodByEventType = new HashMap<>();
//订阅者可接受的
//key对应字符串Method名称+">"+事件类型名称,value对应Method的声明类
final Map subscriberClassByMethodKey = new HashMap<>();
//拼接
final StringBuilder methodKeyBuilder = new StringBuilder(128);
//订阅者的类型
Class> subscriberClass;
//记录的当前的类
Class> clazz;
//是否跳过父类
boolean skipSuperClasses;
//索引
SubscriberInfo subscriberInfo;
//初始化
void initForSubscriber(Class> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
//回收资源
void recycle() {
subscriberMethods.clear();
anyMethodByEventType.clear();
subscriberClassByMethodKey.clear();
methodKeyBuilder.setLength(0);
subscriberClass = null;
clazz = null;
skipSuperClasses = false;
subscriberInfo = null;
}
//通过方法和事件类型是否能够添加到队列中
boolean checkAdd(Method method, Class> eventType) {
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
//集合中之前未添加过这个事件
return true;
} else {
if (existing instanceof Method) {
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
// Paranoia check
//迷之校验,↑原文注释都在吐槽了
throw new IllegalStateException();
}
anyMethodByEventType.put(eventType, this);
}
return checkAddWithMethodSignature(method, eventType);
}
}
//通过方法和事件类型是否能够添加到队列中,二次校验
private boolean checkAddWithMethodSignature(Method method, Class> eventType) {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
//按照指定格式拼接作为String key
String methodKey = methodKeyBuilder.toString();
Class> methodClass = method.getDeclaringClass();
Class> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
//集合未添加过 || 添加过并且后续添加的订阅者方法声明类和之前覆盖的值有子父关系或者同属一类
return true;
} else {
//添加过了且没有类关系
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
//移动到父类
void moveToSuperclass() {
if (skipSuperClasses) {
clazz = null;
} else {
clazz = clazz.getSuperclass();
String clazzName = clazz.getName();
//跳过系统类
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
clazz = null;
}
}
}
}
理解起来比较鸡肋,没什么营养,我们结合findUsingReflectionInSingleClass
一起来看
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
//获取当前类中的声明方法集合
//原文注释说比下面的效率高
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 (Method method : methods) {
//开始遍历
//方法修饰符
int modifiers = method.getModifiers();
//方法只能是public,并且不能为static,abstract和2个笔者不懂的迷之修饰符_(:з」∠)_
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//获取方法需要的参数
Class>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
//一个参数
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class> eventType = parameterTypes[0];
//判断是否可以添加
if (findState.checkAdd(method, eventType)) {
//提取注解的线程修饰mode
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");
}
}
}
仔细一看就明白多了,在findUsingReflection
方法中先初始化了一个FindState
对象,然后对订阅者类型的父子继承关系以及事件类型的匹配进行层层筛选,将筛选结果作为List
返回给上级,筛选过程比较复杂,感兴趣的同学可以参照笔者上面的注释仔细品味。
总结如下:
- 订阅的方法需要
@Subscribe
修饰,里面可以附带线程模式和和优先级; - 订阅方法必须为public,不能为abstract和static;
- 允许订阅者之间的继承关系;
其中SubscriberMethod
内部存放一些事件的信息,我们一会再提
public class SubscriberMethod {
final Method method;
final ThreadMode threadMode;
final Class> eventType;
final int priority;
final boolean sticky;
/** Used for efficient comparison */
String methodString;
}
在将结果返回的方法getMethodsAndRelease
中,还做了缓存处理
private List getMethodsAndRelease(FindState findState) {
List subscriberMethods = new ArrayList<>(findState.subscriberMethods);
findState.recycle();
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
if (FIND_STATE_POOL[i] == null) {
FIND_STATE_POOL[i] = findState;
break;
}
}
}
return subscriberMethods;
}
好了,万里长征已经进行了一半了,什么!?你和我说才一半
(╯‵□′)╯︵┻━┻。。。坚持一下,马上就革命胜利了
┬─┬ ノ( ' - 'ノ)
我们回到EventBus的register
方法,下一步是加锁遍历搜寻器返回的结果,也就是小刚
要把小明
,小张
的订阅表去订阅道指定专栏
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//获取事件类型
Class> eventType = subscriberMethod.eventType;
//一重简单的封装
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//从这个事件类型的map中取出订阅者们
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
//第一次的初始化
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
//已经添加过,抛出异常
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
//根据订阅者方法的优先级存放
subscriptions.add(i, newSubscription);
break;
}
}
//将事件类型放入这个订阅者所订阅的类型集合
List> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) {
//订阅方法是接受粘性事件时
if (eventInheritance) {
//消息允许继承时
//遍历map
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, 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
方法中对事件和订阅者的关系都保存在了map中,然后对粘性事件进行了处理。
这里我们又遇到了粘性事件,打个比方,小张
订阅了音乐
频道的报纸,想在订阅时希望过往的报纸也能看到。其实和四大组件的粘性广播相似,又比如我们开启一个Activity,我们在Activity的onCreate
中注册了事件,但是在传递数据时我们的消息已经发出了,而Activity的初始化不是startActivity
一下就能开启的,所以我们在Activity中的订阅方法要改一改,将注解改成@Subscribe(sticky = true)
,就能接受到粘性事件。
那么EventBus的注册方法我们从头到尾看了一遍,接下来我们来看看分发方法post
public void post(Object event) {
//获取当前线程的状态
PostingThreadState postingState = currentPostingThreadState.get();
//当前线程的消息队列
List
post
方法很简单,队列中有消息就发送出去,如何获取当前线程的状态原理基于ThreadLocal
来实现,这里笔者简要提一下好了,ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储一些数据,而这段数据是不与其他线程共享的。内部原理是通过泛型对象数组,在不同的线程会有不同的数组索引值,这样就可以在不同线程这种调用get
方法时,取到对应线程的数据。
有关PostingThreadState
代码如下
private final ThreadLocal currentPostingThreadState = new ThreadLocal() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
final static class PostingThreadState {
final List
我们继续顺藤摸瓜来看看发送消息的具体实现
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class> eventClass = event.getClass();
//事件是否找到对应订阅者
boolean subscriptionFound = false;
if (eventInheritance) {
//允许事件继承的时候,找到它的父类们
List> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class> clazz = eventTypes.get(h);
//按位或,把结果赋值给subscriptionFound
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
//EventBus发送一个没人认领的事件555。。。
post(new NoSubscriberEvent(this, event));
}
}
}
原来这个方法还不是真身,只是对消息类型进行了继承关系的判断,并且获取到消息发送结果,如果无订阅者订阅则发送一个特定事件,那么再来看看postSingleEventForEventType
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class> eventClass) {
CopyOnWriteArrayList subscriptions;
synchronized (this) {
//同步将能接受到这个消息类型的订阅者们取出来
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
//遍历
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
//将消息发送给订阅者
postToSubscription(subscription, event, postingState.isMainThread);
//消息是否被高优先级的订阅者中断
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
//中断的话这个消息类型后续订阅者就接受不到了
break;
}
}
return true;
}
return false;
}
果然,EventBus按照这个类型的消息依次发送给订阅者们,高优先级的订阅者甚至可以中断低优先级的订阅者们,那么我们来看消息是怎么调度给订阅者的呢
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
//订阅者定义的接受类型
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
我们看到了四种类型
- POSTING:默认线程环境,直接调用
invokeSubscriber
方法,表示在post
消息的线程环境直接执行订阅者的订阅方法 - MAIN:如果当前在主线程中,那么直接执行,否则就切到主线程去响应事件
- BACKGROUND:如果当前在工作线程中,那么直接执行,否则就转到工作线程响应事件
- ASYNC:不管
post
线程情况,直接切到一个新线程中处理事件
事件响应线程和优先级0-100都可以在注解中配置
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
boolean sticky() default false;
int priority() default 0;
}
其中我们先来看看invokeSubscriber
void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
果然,直接通过反射直接调用了invoke
,那么MAIN模式是怎么切换的呢,还是老样子,我们从入口来
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
可以从命名猜测HandlerPoster
内部维护了一个队列
//EventBus调用的入口
void enqueue(Subscription subscription, Object event) {
//从后续队列池中获取一条
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
//再次校验,有点像DCL单例。
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
//反射执行
eventBus.invokeSubscriber(pendingPost);
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
//保证2条EventBus之间处理消息间隔不小于10s,防止主线程卡顿
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
看来最后涉及线程间调度后处理消息还有猫腻,我们在PendingPost
里瞧瞧看
final class PendingPost {
//待处理的 事件对应订阅者的 请求池
private final static List pendingPostPool = new ArrayList();
Object event;
Subscription subscription;
PendingPost next;
private PendingPost(Object event, Subscription subscription) {
this.event = event;
this.subscription = subscription;
}
//
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
if (size > 0) {
//同步在池中取出一个对象池
PendingPost pendingPost = pendingPostPool.remove(size - 1);
pendingPost.event = event;
pendingPost.subscription = subscription;
pendingPost.next = null;
return pendingPost;
}
}
return new PendingPost(event, subscription);
}
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
//池中增长速度不要过快,1000数量的阈值
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost);
}
}
}
}
这么一看好像就只是个对象池嘛,不知道有没有分析错,敬请dalao指正,那么BACKGROUND
和ASYNC
我们也就快速过一遍好了,上文概要的提到这2个mode其实都是实现Runnable接口,只不过BACKGROUND
内部的run方法是同步单线程处理的,而ASYNC
是相当于多线程的。
那么Eventbus的post事件我们已经了解完了原理,无非就是遍历所有该处理这个事件的订阅者,然后根据方法的注解来分配到指定的线程环境中invoke
执行,那么发送粘性事件是在怎么样的呢
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
//也是调用post
post(event);
}
其实本质发送粘性事件和普通发送是一个原理,只不过追加了这条事件到粘性队列中,而后来订阅者如果要响应之前发的粘性事件那么就应该是在订阅的时候,就是在subscribe
方法下的checkPostStickyEventToSubscription
,方法内部直接调用postToSubscription
。
那么EventBus的一套正常流程我们就基本分析完了(°∀°)ノ其余还有一些EventBus的取消后续事件cancelEventDelivery
,移除粘性事件removeStickyEvent
,反注册unregister
,都比较简单,这里就不分析了。
Plus
你们以为这就把EventBus源码分析完了,当然没有!EventBus还有一个必杀技我们还没分析,综上所述,EventBus对订阅者信息多出用到了反射,这是在一定程度上消耗性能的,那么EventBus的开源作者是怎么优化的呢,没错就是通过预编译技术来生成索引,相像一下,我们每次要查询一个单词就要从头去字典找,如果现在有个字典的目录,我们不是就能很快的找到吗?
这张据说是开源作者博客里的性能对比图,可以看到加了索引的EventBus性能简直就是打了鸡血,蹭蹭蹭的上去。
完整依赖如下
在项目根目录gradle中导入apt编译插件
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
Moudle处
apply plugin: 'com.neenbedankt.android-apt'
apt {
arguments {
//改成你的索引
eventBusIndex "com.tk.test.EventBusIndex"
}
}
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
编译过后我们会在这个路径下找到索引app>build>generated>source>apt>debug>com.tk.test.EventBusIndex
/** This class is generated by EventBus, do not edit. */
public class EventBusIndex implements SubscriberInfoIndex {
private static final Map, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(com.tk.test.activity.SplashActivity.class, true, new SubscriberMethodInfo[] {
//通过apt的方式直接把方法名等等参数全放这里了
new SubscriberMethodInfo("onEventReceive", String.class, ThreadMode.POSTING, 0, true),
}));
}
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;
}
}
}
//初始化导入索引
EventBus.builder()
.addIndex(new EventBusIndex())
.installDefaultEventBus();
在EventBusIndex
只有一条记录,因为笔者只有一个Activity作为订阅者╮(╯▽╰)╭,那么索引又是在哪里被读取到EventBus的map中呢,我们再回过头来看看搜寻器的findSubscriberMethods
方法,在里面有一个从索引中寻找的方法findUsingInfo
private List findUsingInfo(Class> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
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);
}
private SubscriberInfo getSubscriberInfo(SubscriberMethodFinder.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) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
//遍历索引
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
其实基本和这个方法findUsingReflection
雷同,然后从FindState的subscriberInfo
或者builder中的索引集合中取出来,然后遍历索引再进行筛选。后面的流程就一样了
apt笔者不太懂,这里就抛砖引玉了,大抵就是编译时把之前的搜寻器SubscriberMethodFinder
的扫描过程以另外一种模式又执行了一遍,然后将结果生成了一个类,原理和大名鼎鼎的ButterKnife类似。
Ps:EventBus在开发中可以封装成一个统一的抽象Event或者接口,订阅者们通过消息的key来区分;
如果对RxJava了解比较深入的同学可以用RxBus来代替,能节省一些代码
这几天学习源码的过程有点累,文章如有错误,敬请指正!
最后再上一张笔者整理的流程图
耶稣说:“我就是道路、真理、生命;若不藉着我,没有人能到父那里去。 (约翰福音 14:6 和合本)