Android EventBus源码分析,基于最新3.1.1版本,看这一篇就够了!!
前言
上一篇文章对EventBus进行了一个介绍,并且对它的使用方式作了一个较全面的分析,建议在阅读本文之前,先看看上篇文章的内容:EventBus使用(全面分析,细节提醒)
本篇文章主要内容是对EventBus的源码进行分析,看看它到底内部是用了什么方式,来实现事件传递,达到如此强大的效果。同是通过对其源码的分析,有助于加深对EventBus的理解使用,让我们以后在开发过程中更加合适的使用它。
通过上篇文章的介绍,我们知道使用EventBus,主要的就是三个方面:
- 订阅者(Subscriber)的register,unregister
- 发布者(publisher)通过post,postSticky方法发送事件
- 通过subscriber注解定义的方法来接收事件
我们就通过这几方面进行,切入查看他的源码实现。
注册
EventBus.getDefault().register(MainActivity.this);
我们通过该语句来对Subscriber进行注册操作。
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
此部分代码块,很好理解,通过getDefault方法获取到EventBus的实例,可以看到它是通过双重校验锁的方式,获取到一个全局的实例,所以在各个组件中需要EventBus实例的地方,直接通过该方法就可以获取到,很是方便。保证了订阅者和发布者都是用的同一个实例。
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
EventBus的构造方法,有两个,但实际上来说,是一个。就算是调用无参的构造函数,它的内部还会调用EventBus(EventBusBuilder builder),这个构造函数。
从上面的代码,我们知道EventBus的构造方式是通过Builder建造者模式实现的。在其有参数的构造方法中可以看到有一些对象的命令方式会可以大概猜出来这个变量会应用在哪些部分,大概有什么作用。(ps:在开发中命名规则是很重要的事情,一个好的命名方式,方便他人,也方便自己)
举个栗子:
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private final Map<Class<?>, Object> stickyEvents;
这三个容器类,通过他的命名方式,我们可以大致猜出些有用的内容。
subscriptionsByEventType: 这是一个Map容器,key的类型是Class对象,value 是list容器,再结合命名方式,所以EventType应该是key,EventType是传递的事件类型,Subscription是新定义的类。。。巴拉巴拉。。等等。。要自然而然会想到一些可能的情况,这有助于接下来分析。
再比如:mainThreadPoster,backgroundPoster,asyncPoster这几个变量的命名方式,就会让人不自由的联想到EventBus里面ThreadMode,有几种模式与这几个变量名很***像***
mainThreadPoster————>ThreadMode.MAIN
backgroundPoster———>ThreadMode.BACKGROUND
asyncPoster —————>ThreadMode.ASYNC
这样一相比较,那八九不离十,这其中有问题啊,而且他们都是以Poster结尾的,自然又会想到EventBus通过post方式来发送事件。。。
好了,说这么些废话,就是想表示看源码的揣测,蒙猜。。。
EventBus通过**register(Object subscriber)**来对Subscriber(订阅者)进行注册。
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
主要看这行代码:
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
这是通过subscriberMethodFinder对象,来找到加了Subscriber注解,接收事件的方法集合的。
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;
}
}
它会先从缓存中读是否有缓存,没有的话接着对ignoreGeneratedIndex进行判断,通过默认的方式获取Eventbus,注册订阅者的话,ignoreGeneratedIndex的值为false,所以这里的值是false。
private List<SubscriberMethod> 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);
}
这里先通过prepareFindState()初始化一个FindState,FindState是什么?他可以理解成是帮助我们找到订阅方法集合的一个辅助类。(ps:订阅方法说的是Subscriber(订阅者)中,用@Subscriber注解修饰的方法,来接收事件消息的。)(pps:刚想说什么来着,忘了。。。)
接着把Subscriber的class对象通过initForSubscriber传递进去,while循环条件成了,getSubscriberInfo()返回null,不成立,会进入findUsingReflectionInSingleClass(findState);方法查找subscribeMethods。这两个条件的值,很好确定,篇幅有限就挑重点的说了。
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();
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.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");
}
}
}
这个方法就是一个比较重要的方法了,它通过反射的方法来把订阅者中的订阅方法给找到,并且对订阅方法的信息进行包装后(封装成SubscriberMethod),添加至容器中。
这个方法虽然看似代码量很多,但是他的代码逻辑不难,并且源码中还有注释,所以就不一句句的分析了,挑一些点来说明一下。
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0)
通过这个代码,我们明白了,为什么之前在创建订阅方法的时候,强调说方法的修饰符必须是Public的,不能是static的,abstract的。
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1)
这俩句代码让我们知道了,在上一篇文章中,为什么会提示说订阅方法的参数个数要是1个。
接着看,在成功找到构建出SubscriberMethod时,还有一个检查。
boolean checkAdd(Method method, Class<?> eventType) {
// 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
// Usually a subscriber doesn't have methods listening to the same event type.
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();
}
// Put any non-Method object to "consume" the existing Method
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 methodKey = methodKeyBuilder.toString();
Class<?> methodClass = method.getDeclaringClass();
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
return true;
} else {
// Revert the put, old class is further down the class hierarchy
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
这个检查订阅方法是否已经存在,有两层检查,大多数情况下,我们只用到一层检查就够了,因为要方法名一致和输入参数类型也是一样的,在同一个类中根本就不行的,编译期都过不去。就像这样:
class A{
....
@Subscribe
public void onReceivedMsg(Event type){
}
@Subscribe
public void onReceivedMsg(Event type){
}
....
}
这样是不行的,那就可能产生疑惑,那么什么情况下会用到第二层的check呢?是啊,一个类中不可能存在两个方法它们的方法名和参数都一样啊,如果是重载的话那是方法名一样,参数又不一样。难道是?没错,这种情况指的就是继承。
经过了层层查找检查,找到了订阅方法,并且封装成SubscriberMethod类,添加到容器中。在该类中找完之后它会接着判断订阅者有没有父类,如果有,它会向父类查找订阅方法。这就跟checkAdd方法的第二层check遥相呼应了。
void moveToSuperclass() {
if (skipSuperClasses) {
clazz = null;
} else {
clazz = clazz.getSuperclass();
String clazzName = clazz.getName();
/** Skip system classes, this just degrades performance. */
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
clazz = null;
}
}
}
这样,查找订阅方法的流程就结束了,所查找到的订阅方法会放在一个集合容器中,并缓存起来,最后return出去。
List<SubscriberMethod> findSubscriberMethods(Class<?> 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;
}
}
现在我们再回过头看之前的register代码:
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
通过subscriberMethodFinder.findSubscriberMethods方法找到订阅方法集合后,还需要对每个订阅方法进行一个subscribe方法操作。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> 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<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
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);
}
}
}
我们来看下代码,虽然看起来量比较大,但是逻辑还是比较简单的,首先会根据subscriber和subscriberMethod来构建一个Subscription对象,接着在subscriptionsByEventType Map容器中找到有没有对应key的value,然后走找不到等于null的逻辑。。。逻辑比较简单,不说了,简单总结一下就是对两个容器的添加Value值,subscriptions容器添加封装好的subscription,subscribedEvents容器添加eventType。还有一个就是对于黏性事件的处理,黏性事件对应于postSticky等sticky系列方法的。这个后面再说。
所以这个subscribe方法作用就是对吧特定对象添加到容器中。到此,关于EventBus的订阅部分的源码逻辑我们就说完了。简单绘制一个注册流程图,把一些重要的方法,点表示一下。
下面我们来看,发送事件的源码逻辑。
Post Event
注册逻辑完成后,订阅者,订阅方法都已经准备好了,现在就差事件发送者了,所以我们接下来就来看看,Publisher(事件发布者)是怎么发送事件消息到订阅方法的。
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
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;
}
}
}
我们看代码,通过ThreadLocal来获得一个线程专属的PostingThreadState对象,然后把事件(Event)添加到队列中。对postingState的一些变量进行赋值操作。走进while循环,判断队列是否为空,不为空走postSingleEvent方法。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
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) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
我们重点看几个点,evenInheritance,通过这个变量名我们大概也能猜到是跟继承有关。这个变量默认值是true,表示向上查找事件的父类,也包含接口。这个变量可以通过
public EventBusBuilder eventInheritance(boolean eventInheritance) {
this.eventInheritance = eventInheritance;
return this;
}
方法设置为false。如果不需要对事件进行向上查找的话,可以吧该值设置为false,设置为false的话,就不需要考虑事件的父类,实现的接口等问题了,效率就自然高了起来。对于简单的事件类型,可以提升百分之20的效率,对于复杂的事件类型所提升的效率还会超过百分之20。不过官方也说,一般来说没必要设置为false,因为本来eventbus传递事件就很快,对CPU的消耗就很低,除非你是每秒发送成百上千的事件。。。。
不扯淡了,接着看,默认eventInheritance的值是true。它会找到事件的所有事件类型。
/** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
synchronized (eventTypesCache) {
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<>();
Class<?> clazz = eventClass;
while (clazz != null) {
eventTypes.add(clazz);
addInterfaces(eventTypes, clazz.getInterfaces());
clazz = clazz.getSuperclass();
}
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}
逻辑比较简单就不说了,找到所有的事件类型之后,就会走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;
}
在这里,我们看到了之前注册的逻辑,subscribe方法中当时就是添加了subscription进入容器中。我们现在这里,找到了它的用处。我们重点看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);
}
}
这个方法,是事件发布者,发送事件消息的核心方法,我们看这个方法,看到这5个case,是不是很熟悉,这不就是对应EventBus 的5中ThreadMode嘛!我们分别看看每个case对应的实现吧!建议看这个方法实现的话需要结合EventBus五种线程模式代表的意思来看,会更好理解,如果你现在已经忘了,EventBus的5种线程模式分别代表什么意思,那就看看我的上篇文章 EventBus 使用(全面分析,细节提醒) 回顾复习一下吧。
Posting
如果订阅方法的订阅线程模式是posting模式的话,是Publisher(事件发布者)和Subscriber(事件接受者)在同一个线程环境下的。
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);
}
}
所以这种case下,它是直接通过反射来invoke订阅方法,哦,原来是反射,看到反射就很熟悉,因为之前订阅逻辑中查找订阅方法也是通过反射的方式来找到的。
MAIN
这是我们熟悉的ThreadMode,它的对应逻辑是这样的:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
这里说一下isMainThread变量是哪个的main Thread,因为MAIN ThreadMode下面又有isMainThread的判断,就很容易让人搞混。switch case语句中,case对应的各种线程模式,是订阅方法设置的线程模式,是属于订阅方法的。isMainThread 所表示的是事件发布者,调用post方法来发送事件时,当时的线程环境是否是主线程环境。
接着看,如果订阅方法的ThreadMode是MAIN、post事件的线程环境是主线程的话,那就像Posting模式一样,直接调用invokeSubscriber,走反射的逻辑,把事件传递给订阅方法。否则就是走mainThreadPoster.enqueue(subscription, event);逻辑。这里提一嘴mainThreadPoster对象,我们之前有提到过它,当时说看名字猜测和EventBus的五种线程模式中的MAIN mode很像,应该会在事件发布者种跟主线程模式的逻辑有关,这不,让我们猜对了。所以说看源码要发挥想象力的去***猜***。。。
interface Poster {
/**
* Enqueue an event to be posted for a particular subscription.
*
* @param subscription Subscription which will receive the event.
* @param event Event that will be posted to subscribers.
*/
void enqueue(Subscription subscription, Object event);
}
mainThreadPoster是HandlerPoster类型的,HandlerPoster实现了Poster接口。
public class HandlerPoster extends Handler implements Poster {
...
...
public 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) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
...
...
}
通过上面的代码,我们知道HandlerPoster继承了Handler,看到这个地方我会不由的想到Android handler消息处理机制,哦,原来EventBus里的跨线程通信,底层也是通过handler实现的吗???
我会进而想看看它的构造方法中的looper对象是从哪传来的,如果是从主线程传的main looper,那岂不正对应这种情况:post方式非主线程,订阅方法的线程模式是主线程。
查找后我们发现,确实传进来的是main looper!
Object getAndroidMainLooperOrNull() {
try {
return Looper.getMainLooper();
} catch (RuntimeException e) {
// Not really a functional Android (e.g. "Stub!" maven dependencies)
return null;
}
}
好的,这波分析,先放在一边,大概心里有数了,我们回到前面
mainThreadPoster.enqueue(subscription, event);
看看 HandlerPoster 的enqueue方法。该方法中构造了一个PendingPost对象并add至队列中。然后发一个message消息。然后在HandlerPoster的handleMessage方法中处理发送过来的消息,我们重点看这行代码
eventBus.invokeSubscriber(pendingPost);
很熟悉吧,这不是调用反射方法的那个方法嘛,再结合之前分析的传入的looper是main looper,这个handleMessage是执行在主线程的,原来原因就是为了调用invokeSubscriber方法,这就是实现了跨线程操作。
MAIN_ORDERED
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
这个模式,有了前面两个case的分析,现在来看就比较简单了,就不分析了。
BACKGROUND
我们回忆下ThreadMode是BACKGROUND时的含义:
post发布环境是主线程的话,事件接收处理的环境是一个子线程。
post发布环境是子线程的话,事件接收处理环境和post发布环境一样。
有了这个基础后,我们再看看代码逻辑。
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
如果不是主线程的话,直接调用invokeSubscriber(subscription, event);方法,没毛病。如果是主线程,会调用BackgroundPoster的enqueue方法,这个BackgroundPoster类也是实现了Poster接口的。
final class BackgroundPoster implements Runnable, Poster {
private final PendingPostQueue queue;
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
eventBus.getExecutorService().execute(this);
}
}
}
@Override
public void run() {
try {
try {
while (true) {
PendingPost pendingPost = queue.poll(1000);
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
executorRunning = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
}
BackgroundPoster 类的代码比较少,就全贴了,方便看。我们看到BackgroundPoster他不仅实现了Poster接口,他还实现了Runnale接口,看到Runnale接口,又会不自觉的想到些什么。。比如Handler的post方法,runnale和线程,线程池的配合使用等等。
我们看它enqueue方法跟之前HandlerPoster 的enqueue方法很像,一个套路。也是构建个PendingPost对象入队。然后我们重点看
eventBus.getExecutorService().execute(this);
这个语句,这是调用了EventBus中的线程池,来执行这个Runnale对象(手动狗头)。这个EventBus中的线程池是通过EventBusBuilder创建传进来的,默认是创建一个CacheThreadPool。当然我们也可以根据需求自己创建传进来。
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
接着,我们看这个run方法,它的核心方法 eventBus.invokeSubscriber(pendingPost); 和HandlerPoster里面的handleMessage方法中一样,调用相同的方法,不同的是它们调用invokeSubscriber的线程环境不一样,一个是主线程,一个是子线程。
ASYNC
asyncPoster.enqueue(subscription, event);
无论post是什么线程环境,订阅方法的线程环境都是子线程。
class AsyncPoster implements Runnable, Poster {
private final PendingPostQueue queue;
private final EventBus eventBus;
AsyncPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
queue.enqueue(pendingPost);
eventBus.getExecutorService().execute(this);
}
@Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
}
也是通过CacheThreadPool线程池来实现的,和上一个比较像,就不在赘述了。
到此,EventBus中的事件发布流程说完了。
简单绘制个流程图梳理下:
unregister
订阅者(subscriber)的register,发布者(publisher)post,订阅者(subscriber)的unregister EventBus主要三大部分,已经说了前两个,现在来看看unregister这一部分。
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 {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
unregister这部分的内荣比较简单,总的来说就是把之前注册的时候往容器中添加的什么事件类型啊,订阅类型,在一个个移除去就行了。
看上面的代码,typesBySubscriber这个map容器,是不是很熟悉,我们之前subscribe 订阅的时候,就用到了它,当时是创建它的value值,往它的value值里面添加内容,现在是remove罢了。代码贴出来大家看到,就明白了,这里就不解释了。
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--;
}
}
}
}
至此,关于EventBus的源码解析基本来说是说完了,因为EventBus 他的核心内容,架构,就是这三部分,订阅方(Subscriber),发布者 Post 事件(Publisher),事件(Event)。我们再来看下官网的图片感受一下。
其他的一些可能没说到的细节点,在你明白这三部分的源码逻辑后,都将自然而然的明白。
补充一下之前在subscribe方法中遇到的sticky(粘性事件问题)
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);
}
}
他配合postSticky方法,订阅方法中的@Subscribe注解中sticky参数要设置为true等条件。来使用。
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
我们发现,如果是粘性事件的话,它在subscribe订阅的时候就开始post 事件了。通过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, isMainThread());
}
}
然后他里面的实现逻辑还是
postToSubscription 通过这个方法来post 事件,跟前面的一样,只不过post的时机不一样了。粘性事件它在订阅(register)的时候会post一次,在postSticky的时候还会post一次,这就让它产生了粘性(sticky) 的特性了。
结语
关于EventBus源码分析就说这些,文章中出了分析EventBus的源码实现,也介绍了本人看源码的一些思路心得。希望对大家能有用。如果有一些不一样的看法或者需要补充的点,可以在留言区留言发表观点。大家共同学习进步!!