EventBus 源码解析
先介绍控件使用方法,然后再从基本的使用方法断点调试,整体了解一下流程。
EventBus 基本使用
在 module 的 build.gradle添加
implementation 'org.greenrobot:eventbus:3.1.1'
在接收消息的地方注册 eventBus
EventBus.getDefault().register(this);
然后创建一个事件类
public class FirstEvent {
public String message;
public FirstEvent(String message){
this.message = message;
}
}
在注册 EventBus 的类中创建接收事件方法
// 这里的threadMode可以设置不同的值,在不同的线程中处理事件接收
@Subscribe(threadMode = ThreadMode.MAIN)
public void setText(FirstEvent event){
textView.setText(event.message);
}
然后在任意一个地方发送一个消息
EventBus.getDefault().post(new FirstEvent("来自第某个地方的消息"));
EventBus一个基本的流程就这样完成了,接下来看每一步的源码
注册流程源码
EventBus.getDefault()
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
这是一个单例方法,用的是 DoubleCheck 的方式,创建了一个 EventBus 的实例。
register()
public void register(Object subscriber) {
Class> subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
简单来看,register 方法是将注册的类与该类中事件处理的方法进行一个绑定。
SubscriberMethod 类是一个实体类,将订阅相关的数据进行一个封装:
然后我们看一下
subscriberMethodFinder.findSubscriberMethods() 方法
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;
}
}
findSubscriberMethods 方法会先从 METHOD__CACHE中查找,没有找到则会根据 ignoreGeneratedIndex 标志选择是采用反射的方式寻找还是用 findUsingInfo 方法,这里这个标志和 findUsingInfo 方法目前都不知道是什么。找到后会将结果加入到缓存中。
接下来就是 subscribe() 方法
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
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);
// 后面是粘性事件相关处理,先不管
...
}
subscribe 方法主要做两件事,一个是 subscriptionsByEventType.put(eventType, subscriptions) ,subscriptionsByEventType 是以事件的类为 key,订阅者的回调方法为 value 的映射关系表。
另一个是 typesBySubscriber.put(subscriber, subscribedEvents) , typesBySubscriber 是以订阅者为 key, 订阅者订阅的事件类为 value 的映射关系表。
到这里一个 EventBus 的注册流程就结束了,从这个流程来看,主要就是建立事件类和订阅者之间的映射关系。
注销流程源码
unregister()
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 {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
注销流程就很简单了,先取出该订阅者对应的订阅事件类,然后遍历订阅的事件类,再取出该类所有的订阅者,把我们注销的订阅者移除,最后再把注销的这个订阅者从 typesBySubscriber 中移除。
Post 流程
先看下post的流程图
然后解析每个步骤,看下都做了哪些操作
post ()
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List
PostingThreadState 的实例存储在 ThreadLocal 中,是一个内部静态类,将事件队列,post 状态,事件和订阅者等信息封装在一起。post 方法取出当前线程的PostingThreadState,将事件添加到事件队列中,设置一些状态信息,然后循环把事件队列(用一个 ArrayList 实现)的事件取出来分发。
postSingleEvent()
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class> eventClass = event.getClass();
boolean subscriptionFound = false;
// 标志位,决定是否要post事件所有父类和实现接口
if (eventInheritance) {
/* lookupAllEventTypes 方法是找到该事件的所有父类和所有实现的接口*/
List> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
...
// 事件没有订阅者就打打日志啥的
}
}
postSingleEvent 方法就两个逻辑,一个是决定是否要post事件所有父类和实现接口,另一个就是事件没有订阅者,会判断一些条件,然后重新 post 一个 NoSubscriberEvent 事件。
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;
}
没啥多说的,根据传过来的事件,拿到对应的订阅关系,PostingThreadState 中记录信息,然后交给 postToSubscription 方法。
postToSubscription ()
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 MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(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);
}
}
最后根据订阅者的回调方法设置的线程,决定再什么线程上反射调用该回调方法,反射调用通过 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);
}
}
这里我们重点关注 EventBus 是如何让事件回调在不同线程中执行的:
- POSTTING: 不用多说,再哪个线程抛出来的事件,就再哪个线程直接反射调用回调方法,不涉及线程切换
- MAIN: 在主线程中反射调用回调事件,如果当前线程不是主线程,则通过 mainThreadPoster 插入一个事件, mainThreadPoster 是通过主线程 Looper 创建的一个Handler,调用 enqueue() 方法,会通过自身发一个消息,然后在自己的 handleMessage() 方法中反射调用事件的回调方法,这样就完成了在主线程回调。
- MAIN_ORDERED: 也是在主线程中反射调用回调方法,但是有顺序,不会发生第二个事件早于第一个事件执行完。
- BACKGROUND: 如果当前线程是主线程,则将事件添加到 backgroundPoster 中,backgroundPoster的 enqueue() 方法会通过线程池执行一个任务,反射调用回调方法。
- ASYNC: 无论在哪个线程,都交给线程池去处理这个事件。
到这,Post 流程分析基本完成。