本文要解決的問題
1.什么是粘性事件?如何實(shí)現(xiàn)的渠脉?
2.Eventbus內(nèi)部線程調(diào)度是如何實(shí)現(xiàn)的堤魁?
什么是粘性事件委乌?如何實(shí)現(xiàn)的添谊?
粘性事件,是指在發(fā)送事件之后再訂閱該事件也能收到該事件残制,這就使得我們可以預(yù)先處理一些事件立砸,讓有消費(fèi)者時(shí)再把這些事件投遞給消費(fèi)者.
發(fā)送粘性事件
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);
}
通過源碼得知當(dāng)粘性事件發(fā)送之后就會被存儲到stickyEvents隊(duì)列里面,當(dāng)有相對應(yīng)的訂閱的時(shí)候才會收到粘性事件初茶。
//當(dāng)訂閱的事件為粘性時(shí) 執(zhí)行以下方法
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();
//執(zhí)行對應(yīng)的訂閱方法
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
當(dāng)訂閱方法颗祝,粘性參數(shù)為true的時(shí)候,會去檢索粘性事件存儲隊(duì)列恼布,當(dāng)隊(duì)列有匹配的對象的時(shí)候執(zhí)行訂閱方法螺戳。
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;
}
}
}
移除粘性事件(當(dāng)粘性事件處理過之后,需移除粘性事件折汞,否則這個(gè)事件會一直存儲在隊(duì)列里面倔幼,每次啟動都會執(zhí)行訂閱方法)
我們看到移除粘性事件方法是從stickyEvents隊(duì)列里面找到匹配的對象,然后移除爽待,此事件就不會被重復(fù)執(zhí)行了损同。
Eventbus內(nèi)部線程調(diào)度是如何實(shí)現(xiàn)的?
public enum ThreadMode {
// 事件的處理和事件的發(fā)送在相同的進(jìn)程
POSTING,
//事件的處理會在UI線程執(zhí)行
MAIN,
//后臺進(jìn)程鸟款,處理如保存到數(shù)據(jù)庫
BACKGROUND
//異步執(zhí)行膏燃,另起線程操作,事件的處理會在單獨(dú)的線程執(zhí)行何什,主要用于后臺線程中耗時(shí)操作
ASYNC
}
我們知道eventbus的訂閱方法有以上四種線程模式组哩,那么event是如何從發(fā)送者的線程切換到接收方的線程的呢。
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方法進(jìn)行線程調(diào)度的。
public class HandlerPoster extends Handler implements Poster {}
final class BackgroundPoster implements Runnable, Poster {}
class AsyncPoster implements Runnable, Poster {}
通過上面代碼我們可以得知 mainThreadPoster; backgroundPoster; asyncPoster這三個(gè)類分別繼承自Handler Runnable Runnable伶贰,也就是說EventBus線程切換底層是通過Handle的handleMessage()方法回到主線程蛛砰,通過Runnable的run方法切到子線程。
至此我們已經(jīng)解決以上兩個(gè)問題幕袱。