本文要解决的问题
1.什么是粘性事件?如何实现的?
2.Eventbus内部线程调度是如何实现的?
什么是粘性事件?如何实现的?
粘性事件,是指在发送事件之后再订阅该事件也能收到该事件,这就使得我们可以预先处理一些事件,让有消费者时再把这些事件投递给消费者.
发送粘性事件
public void sticky(View view) {
EventBus.getDefault().postSticky(new EventBean("abc"));
}
接收粘性事件
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void receiveSoundRecongnizedmsg(EventBean bean) {
L.i(bean.getName());
}
存储粘性事件
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队列里面,当有相对应的订阅的时候才会收到粘性事件。
//当订阅的事件为粘性时 执行以下方法
if (subscriberMethod.sticky) {
if (eventInheritance) {
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);
}
}
当订阅方法,粘性参数为true的时候,会去检索粘性事件存储队列,当队列有匹配的对象的时候执行订阅方法。
public boolean removeStickyEvent(Object event) {
synchronized (stickyEvents) {
Class<?> eventType = event.getClass();
Object existingEvent = stickyEvents.get(eventType);
if (event.equals(existingEvent)) {
stickyEvents.remove(eventType);
return true;
} else {
return false;
}
}
}
移除粘性事件(当粘性事件处理过之后,需移除粘性事件,否则这个事件会一直存储在队列里面,每次启动都会执行订阅方法)
我们看到移除粘性事件方法是从stickyEvents队列里面找到匹配的对象,然后移除,此事件就不会被重复执行了。
Eventbus内部线程调度是如何实现的?
public enum ThreadMode {
// 事件的处理和事件的发送在相同的进程
POSTING,
//事件的处理会在UI线程执行
MAIN,
//后台进程,处理如保存到数据库
BACKGROUND
//异步执行,另起线程操作,事件的处理会在单独的线程执行,主要用于后台线程中耗时操作
ASYNC
}
我们知道eventbus的订阅方法有以上四种线程模式,那么event是如何从发送者的线程切换到接收方的线程的呢。
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);
}
}
由此可以得知eventbus是通过 mainThreadPoster; backgroundPoster; asyncPoster的enqueue方法进行线程调度的。
public class HandlerPoster extends Handler implements Poster {}
final class BackgroundPoster implements Runnable, Poster {}
class AsyncPoster implements Runnable, Poster {}
通过上面代码我们可以得知 mainThreadPoster; backgroundPoster; asyncPoster这三个类分别继承自Handler Runnable Runnable,也就是说EventBus线程切换底层是通过Handle的handleMessage()方法回到主线程,通过Runnable的run方法切到子线程。
至此我们已经解决以上两个问题。