文章列表
EventBus源码解析(一)
EventBus源码解析(二)
EventBus源码解析(三)
第一篇文章中我们讲到了事件的注册,本篇文章讲解,事件是如何发送出去的。EventBus的post方法系列。
Post方法
/** Posts the given event to the event bus. */
public void post(Object event) {
//PostingThreadState 存储“发送事件“这个动作的一些信息,这个封装后的集合会存储在线程本地变量中,以防多线程操作时线程之间数据的污染
PostingThreadState postingState = currentPostingThreadState.get();
//将要发送的时间存进postingState的eventQueue变量中
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
//postingState.isPosting是否正在发送、默认为false
if (!postingState.isPosting) {
//检查当前线程是否运行在主线程
postingState.isMainThread = isMainThread();
//修改isPosting为正在发送的状态
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;
}
}
}
核心代码就是postSingleEvent(eventQueue.remove(0), postingState)
,看下这个方法都做了哪些工作
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//是否考虑继承、默认是true
if (eventInheritance) {
//查找eventType继承的类和接口
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
//遍历这些类和接口以及eventType类自己组成的list
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
//分别调用postSingleEventForEventType并与subscriptionFound取"或"操作,然后赋值给subscriptionFound
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));
}
}
}
看一下方法lookupAllEventTypes
/** 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;
}
}
/** Recurses through super interfaces. */
static void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) {
for (Class<?> interfaceClass : interfaces) {
if (!eventTypes.contains(interfaceClass)) {
eventTypes.add(interfaceClass);
addInterfaces(eventTypes, interfaceClass.getInterfaces());
}
}
}
这个方法就是获取eventType
的父类和它继承的接口,把这些都存入到list中,并返回,返回之前将list存进一个以eventType
为Key的map中缓存起来。再回到postSingleEvent
方法中,遍历返回的list分别调用postSingleEventForEventType(event, postingState, clazz)
方法,下面看一下这个方法:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//看到上一篇文章中出现的变量了、根据eventType的class对象取出订阅者信息的对象
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(subscription, event, postingState.isMainThread)
方法
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
方法。信息发送完成返回到方法postSingleEventForEventType
中,订阅当前eventType
的所有的订阅方法的都已完成调用,返回true,回到前一个方法postSingleEvent
。因为是在eventType的 父类及接口集合中,调用的发送信息的方法、所以循环结束,订阅eventType及其父类和接口的事件的方法都已完成调用,看下subscriptionFound
,如果没有一个订阅方法订阅该事件,则此变量为false。会执行后续的代码,否则不会执行,后续代码就是打印log并发送一个“无订阅者订阅”的事件。
从上一篇文章到本篇文章,只是把最基本的订阅发送流程讲通了,还有一些其他的细枝末节没讲到。下面会针对没讲到的查漏补缺。
postSticky方法
发送黏性事件的时候,就不能调用post方法了。而是调用postSticky
,下面我们看一下发送黏性事件的流程:
/**
* Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
* event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
*/
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);
}
变量stickyEvents
用来存储事件,当需要黏性监听方法处理的时候(即我们上一篇文章中,subscribe
方法的末尾有针对黏性监听事件的处理逻辑),就会从这里取事件。然后调用post
方法,走回之前的逻辑。
另外我们看一下postToSubscription
方法针对其他ThreadMode
的订阅方法是如何处理的。
postToSubscription方法:
代码不贴了,看上面的代码就好,不同的模式分别介绍
POSTION
模式:已经介绍过了。
MAIN
模式:此模式下分为两个逻辑,1)如果当前线程为主线程就执行invokeSubscriber
方法,和POSTION
模式一致;2)如果当前不为主线程,则执行mainThreadPoster.enqueue(subscription, event);
这句代码,那mainThreadPoster
这是什么呢?
回过头来先看几个变量:
// @Nullable
private final MainThreadSupport mainThreadSupport;
// @Nullable
private final Poster mainThreadPoster;
//..省略...
EventBus(EventBusBuilder builder) {
//...省略..
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
//...省略..
}
找到mainThreadPoster
定义及初始化的地方,跟进去看看这两个变量是从哪来的,先看EventBusBuilder
的getMainThreadSupport
方法
MainThreadSupport getMainThreadSupport() {
if (mainThreadSupport != null) {
return mainThreadSupport;
} else if (Logger.AndroidLogger.isAndroidLogAvailable()) {//这里找能到'android.util.log'类,所以返回值true
//这里调用下面的方法,其实就是获取AndroidUI线程的Looper对象
Object looperOrNull = getAndroidMainLooperOrNull();
//看这里。会返回一个MainThreadSupport的内部类AndroidHandlerMainThreadSupport的对象
return looperOrNull == null ? null :
new MainThreadSupport.AndroidHandlerMainThreadSupport((Looper) looperOrNull);
} else {
return null;
}
}
Object getAndroidMainLooperOrNull() {
try {
return Looper.getMainLooper();
} catch (RuntimeException e) {
// Not really a functional Android (e.g. "Stub!" maven dependencies)
return null;
}
}
上面注释写的很清楚getMainThreadSupport
方法最终返回了AndroidHandlerMainThreadSupport
对象,回到EventBus的构造方法中,mainThreadSupport
其实就是MainThreadSupport
类的内部类AndroidHandlerMainThreadSupport
的对象,这个对象内部持有AndroidUi线程的Looper的引用。
所以mainThreadPoster
变量其实就是AndroidHandlerMainThreadSupport
对象调用createPoster
方法的返回值,而这个方法返回的就是一个handler,也就是说mainThreadPoster
其实就是一个handler
对象,
贴一下MainThreadSupport
类的代码
public interface MainThreadSupport {
boolean isMainThread();
Poster createPoster(EventBus eventBus);
class AndroidHandlerMainThreadSupport implements MainThreadSupport {
private final Looper looper;
public AndroidHandlerMainThreadSupport(Looper looper) {
this.looper = looper;
}
@Override
public boolean isMainThread() {
return looper == Looper.myLooper();
}
@Override
public Poster createPoster(EventBus eventBus) {
return new HandlerPoster(eventBus, looper, 10);
}
}
}
而这个handler
内部有一个enqueue
方法
public void enqueue(Subscription subscription, Object event) {
//PendingPost可以理解为 是对一个订阅者信息及发送事件封装的 消息体,这个消息体需要放到消息队列中
//PendingPost对象是从PendingPost池中取值
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
//同步处理
synchronized (this) {
//queue是PendingPostQueue类型的,PendingPostQueue可以理解为一个队列,存放PendingPost消息体
//消息入队列操作
queue.enqueue(pendingPost);
//判断handler是否启动
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)方法,此方法内部将pendingPost中的内容解析出来,接着调用invokeSubscriber(Subscription subscription, Object event),代码不再贴出来了
eventBus.invokeSubscriber(pendingPost);
//下面这部分逻辑的意思是:while(true)循环不能超过10毫秒、超过后,就需要停止当前循环,并重新发送消息重新进入handlemessage方法,也就是说handlemessage方法最长只能执行10毫秒
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;
}
}
找到根源了,一句话mainThreadPoster.enqueue(subscription, event);
这个方法就是一个自定义的handler
的对象调用自己的enqueue
方法。将消息体存入队列中,然后由自己来处理、最终调用到invokeSubscriber(Subscription subscription, Object event)
方法中。
BACKGROUND
模式:此模式中主要看backgroundPoster.enqueue(subscription, event);
这句代码做了什么工作:backgroundPoster
就是一个线程对象,也有一个equeue
方法,将将消息入队列,并开启线程工作,然后在run方法中,从队列取出消息体。并调用eventBus的eventBus.invokeSubscriber(pendingPost);
方法,代码不贴了。跟上面那个大体差不多
ASYNC
模式:也是开启一个线程,入队消息,在子线程中取消息执行eventBus.invokeSubscriber(pendingPost);
方法,但是跟Background模式的区别是,消息入队的时候,没有做同步处理,而且发送消息的时候,也没有做同步的检查,而且它也不会等订阅方法执行完再发送下一个消息。
EventBus性能的提升,请看EventBus源码解析(三)———EventBus注册编译期获取订阅者信息