一、什么是EventBus。
最近在项目中使用了EventBus3.0(本文章只针对3.0源码分析),然后想了解原理,于是乎有了这篇文章。先看看官网的解释:
EventBus is an open-source library for Android using the publisher/subscriber pattern for loose coupling. EventBus enables central communication to decoupled classes with just a few lines of code – simplifying the code, removing dependencies, and speeding up app development。
翻译一下:eventbus是一个Android的开源库,它使用了发布/订阅的松散耦合模式。eventbus能使用几行代码完成中心通信–简化代码,去除依赖,加快应用程序的开发。
其实最重要的就是Android的组件之间,例如Activity和Fragment等通信变得简单,减少代码量。下面看看官网的说明图片:
从图中可以看出,Event为事件,Subscriber为事件订阅者,publish为事件发布者,发布事件Post()和订阅事件onEvent()的解耦程度非常高。
二、如何使用EventBus。
在Android Stdio里使用EventBus只需要添加一个依赖:
compile 'rg.greenrobot:eventbus:3.0.0'
EventBus的使用步骤分为三步。第一,在需要订阅事件的类里注册EventBus,EventBus.getDefault().register(this),在OnDestroy()里EventBus.getDefault().unregister(this);
第二,定义处理订阅事件的方法(方法名可自行修改),EventBus3.0是用注解的方式来处理:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMsgEvent(MsgEvent event) {
//处理方法
}
第三,发送事件:
EventBus.getDefault().post(new MsgEvent("message"));
订阅事件的方法有四种线程模式(ThreadMode):
1.POSTING(默认):该事件在哪个线程发布出来的,事件处理 函数就会在这 个线程中运行(注意避免耗时操作)。
2.MAIN: 事件的处理会在UI线程中执行。事件处理时间不能太长,长了会ANR的。
3.BACKGROUND:如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。
4.ASYNC:无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行。
三、源码分析
注册流程:
第一步从注册开始,EventBus.getDefault().register(this);首先看一下EventBus.getDefault():
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
可以看到,EventBus是用单例模式来提供实例的,接下来看register(this)方法:
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
了解该方法前先了解其中的SubscriberMethod是什么和findSubscriberMethods()里面做了什么:
public class SubscriberMethod {
final Method method;
final ThreadMode threadMode;
final Class<?> eventType;
final int priority;
final boolean sticky;
/** Used for efficient comparison */
String methodString;
...
}
没错,它就是一个用来存储订阅信息的实体类,method是订阅的方法,threadMode是执行在哪个线程,eventType是接收的事件类型,priority是设置优先级,sticky表示是否接收粘性事件。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//在缓存中取出订阅者,首次为空
List<SubscriberMethod> 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;
}
}
可以看到,有两种方法来获取订阅类中的订阅方法,第一种是通过注解处理器生成的MyeveventBusIndex来获取,第二种是通过反射来获取。前者在编译期通过读取@subscribe注解并解析来获取订阅者的信息,比后者在运行时使用反射来获取的速度要快一些。下面重点分析如何通过反射来获取订阅信息:
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
这里面先来看看FindState是什么东东:
static class FindState {
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
Class<?> subscriberClass;
Class<?> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
...
}
原来,FindState是一个保存订阅者与订阅方法的相关所有信息的一个实体类,findState.clazz != null就是订阅者不为空时,就执行findUsingReflectionInSingleClass(findState);这一句,接下来进去该方法:
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
//获取订阅者的所有方法
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的方法
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)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
//把当前方法添加到FindState.subscriberMethods里面
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");
}
}
}
该方法就是通过反射获取订阅者信息的核心类,要过滤所有非public而且参数个数不为1的并且需要有注解@subscribe的方法,然后得到一个订阅方法的集合。这样一来前面的findSubscriberMethods()方法就分析完了,接下来到register()方法里面的:
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
下面来看看subscribe()方法:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//获取订阅方法的参数信息
Class<?> eventType = subscriberMethod.eventType;
//Subscription是封装订阅者和订阅方法相关信息的一个实体类
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//根据订阅事件类型eventType从subscriptionsByEventType(一个Map集合)中获取该事件类型对应的所有Subscription信息,首次为空
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
//把所有的订阅者存放入subscriptionsByEventType
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;
}
}
//获取订阅者的所有订阅的事件类型,然后添加到typesBySubscriber(Map集合)
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
//把事件类型添加到typesBySubscriber(Map集合)
subscribedEvents.add(eventType);
//判是否是粘性事件,如果是就取出post给当前订阅者
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, 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);
}
}
}
下面我再贴出其中的Subscription实体类代码:
final class Subscription {
final Object subscriber;
final SubscriberMethod subscriberMethod;
volatile boolean active;
...
}
第一,获取订阅方法的参数类型eventType;第二,根据eventType获取所有的订阅者;第三,根据设置的优先级把对应该事件类型的订阅者添加到指定位置;第四,获取订阅者所有的订阅事件类型并添加到typesBySubscriber中;第五,判断是否为粘性事件,如果是就调用checkPostStickyEventToSubscription()方法把该事件传给订阅者,其中checkPostStickyEventToSubscription()方法的代码如下:
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
// --> Strange corner case, which we don't take care of here.
postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
}
}
可以在该方法里面看到通postToSubscription()方法(该方法下面会讲到)post给订阅者,到此为止,就把注册流程讲完了。
分发流程:
接下来讲事件的分发流程EventBus.getDefault().post( EventType ),我们先看看post()方法:
public void post(Object event) {
//获取当前线程的PostingThreadState
PostingThreadState postingState = currentPostingThreadState.get();
//获取当前线程的事件队列
List<Object> eventQueue = postingState.eventQueue;
//把要分发的事件添加到事件队列中
eventQueue.add(event);
if (!postingState.isPosting) {
//判断当前调用post()的线程是否为主线程
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
//循环分发事件队列中事件
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
该方法主要涉及到PostingThreadState和postSingleEvent()方法,先看源码:
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>();//事件队列
boolean isPosting;//是否在分发
boolean isMainThread;//是否在主线程post
Subscription subscription;//订阅者与订阅事件的实体类
Object event;//订阅事件类型
boolean canceled;//是否取消
}
看到PostingThreadState就是封装了当前线程的事件队列,订阅者与订阅事件等重要信息,方便随后根据订阅者把订阅事件分发过去。 post()方法中拿到事件队列之后就开始循环分发事件:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//获取事件类型
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//是否触发了eventClass的父类及接口类的响应方法,默认为true
if (eventInheritance) {
List<Class<?>> 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) {
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
可以看到该方法里面主要的处理方法就是postSingleEventForEventType(),接下来看源码:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> 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;
}
到这里我们就知道,分发事件调用的方法是postToSubscription()(上面注册流程的最后一步所调用的方法):
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
//判断订阅方法的线程模式
switch (subscription.subscriberMethod.threadMode) {
//默认的线程模式,在发送的线程执行
case POSTING:
invokeSubscriber(subscription, event);
break;
//在UI线程执行
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);
}
}
这里面四种线程模式threadMode对应着各自分发方式,其中invokeSubscriber()就是真正执行事件分发的方法:
private 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);
}
}
我们看到最后原来是通过反射来调用订阅者的订阅方法。
总结:第一,先获取当前线程的PostingThreadState,得到事件队列;第二,把当前post的事件添加到事件队列;第三,循环调用postSingleEvent把事件队列的事件分发出去,最后是通过反射来把事件分发到订阅者的订阅方法。
整个事件分发流程就是这样。
取消注册流程:
最后来看看取消注册的流程EventBus.getDefault().unregister(this):
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());
}
}
下面是unsubscribeByEventType() 方法:
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
//通过事件类型来获取所有订阅者
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
//循环移除所有的订阅者
subscriptions.remove(i);
i--;
size--;
}
}
}
}
到这里,基本流程已经全部过了一遍,希望大家看完能够有更深一步的理解。