上一篇我們針對(duì)
EventBus
的 注冊(cè) 與 注銷(xiāo)做了源碼剖析捎琐,通過(guò)流程圖和對(duì)源碼的剖析,應(yīng)該對(duì)上述流程有了一定了解.
接下來(lái)我們將會(huì)對(duì)Post事件發(fā)布與接收 和 postSticky事件發(fā)布與接收 展開(kāi)剖析否彩,我們將會(huì)提出幾個(gè)問(wèn)題,讓我們帶著問(wèn)題去剖析這兩個(gè)事件的流程嗦随。
我的EventBus系列的其他文章地址
EventBus系列『一』——注冊(cè)與注銷(xiāo)
EventBus系列『二』——Post與postSticky事件的發(fā)布與接收
EventBus系列『番外』——認(rèn)真剖析 『PendingPostQueue』隊(duì)列的實(shí)現(xiàn)思想
問(wèn)題
Post事件
與postSticky事件
的使用場(chǎng)景
Post事件
與postSticky事件
的區(qū)別
postSticky事件
實(shí)現(xiàn) 粘性的原理
剖析
Post事件發(fā)布
我們先上一張
Post事件
發(fā)布流程圖:
我們根據(jù)流程圖的執(zhí)行流程列荔,開(kāi)始我們的對(duì)源碼的剖析:
public void post(Object event) {
//從ThreadLocal?中獲取PostingThreadState類(lèi)實(shí)例
PostingThreadState postingState = currentPostingThreadState.get();
//獲取PostingThreadState的屬性?
List<Object> eventQueue = postingState.eventQueue;
//將event事件添加到eventQueue隊(duì)列?
eventQueue.add(event);?
if (!postingState.isPosting) {
//設(shè)置PostingThreadState的isMainThread屬性?
postingState.isMainThread = isMainThread();
//設(shè)置PostingThreadState的isPosting屬性?
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
//遍歷eventQueue隊(duì)列?
while (!eventQueue.isEmpty()) {
//[ 1 ] ?遍歷隊(duì)列,逐一對(duì)隊(duì)列中的元素執(zhí)行發(fā)布 參數(shù)中 [ eventQueue.remove(0) ] 方法會(huì)將隊(duì)列下的指定位置的元素移除枚尼,并返回被移除的元素值
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
- 1.從ThreadLocal?中獲取PostingThreadState類(lèi)實(shí)例
- ?遍歷隊(duì)列贴浙,逐一對(duì)隊(duì)列中的元素執(zhí)行發(fā)布 參數(shù)中
eventQueue.remove(0)
方法會(huì)將隊(duì)列下的指定位置的元素移除,并返回被移除的元素值
- 最后重置
postingState
狀態(tài)屬性
[ 1 ] ?遍歷隊(duì)列署恍,逐一對(duì)隊(duì)列中的元素執(zhí)行發(fā)布,進(jìn)入postSingleEvent(eventQueue.remove(0), postingState)
方法
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//獲取事件的類(lèi)型class?
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//判定是否是繼承事件?
if (eventInheritance) {
//獲取類(lèi)型class的父類(lèi)集合?
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
//遍歷類(lèi)型class的父類(lèi)集合?
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
//[ 2] 為事件類(lèi)型發(fā)布單個(gè)事件?崎溃,返回的結(jié)果與 subscriptionFound 進(jìn)行 [ 位或運(yùn)算 ] (有1則為1),
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
//為事件類(lèi)型發(fā)布單個(gè)事件??
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
?//若發(fā)布的事件沒(méi)有被訂閱
if (!subscriptionFound) {
//是否打印沒(méi)有訂閱的信息
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
/**
*@重點(diǎn)講解:此處做一次判定
* 1. 若正常發(fā)布的事件沒(méi)有被訂閱盯质,則在此處將 當(dāng)前 eventBust實(shí)例
* 和完成的 event事件封裝成 NoSubscriberEvent類(lèi)型袁串,重新發(fā)送
*2.若沒(méi)有被訂閱的事件已經(jīng)是封裝了NoSubscriberEvent類(lèi)型的事
*件,那么將不會(huì)再次發(fā)布了。
*/
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
// 當(dāng)找不到已發(fā)布事件的訂閱者時(shí)呼巷,EventBus將此事件發(fā)布到事件總線(xiàn)上?
post(new NoSubscriberEvent(this, event));
}
}
}
- 獲取事件類(lèi)型class囱修,判定當(dāng)前事件是不是繼承事件
- 2.若是繼承事件,則獲取類(lèi)型class的父類(lèi)集合王悍,遍歷父類(lèi)集合破镰, 為事件類(lèi)型發(fā)布單個(gè)事件?,返回的結(jié)果與 subscriptionFound 進(jìn)行 [ 位或運(yùn)算 ]
- 3.若不是繼承事件压储,則為事件類(lèi)型發(fā)布單個(gè)事件??
- 判定發(fā)布的事件是否被訂閱
[ 2 ] 為事件類(lèi)型發(fā)布單個(gè)事件? postSingleEventForEventType(event, postingState, eventClass)
方法
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
//通過(guò)注冊(cè)類(lèi)獲取訂閱事件集合?
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 {
//[ 3 ] 發(fā)布訂閱事件?
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;
}
- 通過(guò)注冊(cè)類(lèi)class從
subscriptionsByEventType
獲取訂閱信息集合
(提示:subscriptionsByEventType
集合只有在register注冊(cè)
的時(shí)候才會(huì)執(zhí)行put,此處是post發(fā)布事件
直接從中取值 ).- 2.做判定
subscriptionsByEventType
獲取的數(shù)據(jù)為是否為Null
- 3.若為Null, 表明發(fā)布的事件還沒(méi)有被訂閱芋膘,也就是說(shuō)事件是在執(zhí)行
register注冊(cè)
之前發(fā)布的鳞青,所以沒(méi)有被訂閱上霸饲,所以直接返回False
即可.- 4.若不為Null, 遍歷訂閱信息事件集合?臂拓, 發(fā)布訂閱事件?
- 這里若是發(fā)布的
Post事件
沒(méi)有被訂閱厚脉,則該事件就會(huì) 流轉(zhuǎn),即作廢.- 若發(fā)布的
postSticky事件
沒(méi)有被訂閱胶惰,由于postSticky事件
是將完整的事件放入內(nèi)存的傻工,當(dāng)執(zhí)行register注冊(cè)
時(shí)會(huì)從內(nèi)存中獲取該事件,然后包裝成一個(gè)新的Post事件
進(jìn)行發(fā)布孵滞,由于此時(shí)register注冊(cè)
已執(zhí)行完畢,所以postSticky事件
可以正常使用.
[ 3 ] 發(fā)布訂閱事件? postToSubscription(subscription, event, postingState.isMainThread)
方法
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
//根據(jù)訂閱事件的 threadMode類(lèi)型 進(jìn)行分發(fā)處理?
switch (subscription.subscriberMethod.threadMode) {
case POSTING: // 默認(rèn)的線(xiàn)程模式中捆,在那個(gè)線(xiàn)程發(fā)送事件就在對(duì)應(yīng)線(xiàn)程處理事件,避免了線(xiàn)程切換坊饶,效率高
//[ 3.1 ] 執(zhí)行調(diào)用訂閱者?
invokeSubscriber(subscription, event);
break;
case MAIN: // 如在主線(xiàn)程(UI線(xiàn)程)發(fā)送事件泄伪,則直接在主線(xiàn)程處理事件;如果在子線(xiàn)程發(fā)送事件匿级,則先將事件入隊(duì)列蟋滴,然后通過(guò) Handler 切換到主線(xiàn)程,依次處理事件痘绎。
//判斷是否在主線(xiàn)程發(fā)送分事件
? if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
//[3.2]將訂閱事件放入主線(xiàn)程隊(duì)列津函,依此處理事件?
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED: // 無(wú)論在那個(gè)線(xiàn)程發(fā)送事件,都先將事件入隊(duì)列孤页,然后通過(guò) Handler 切換到主線(xiàn)程尔苦,依次處理事件
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// 若主線(xiàn)程隊(duì)列為空,則直接調(diào)用執(zhí)行訂閱者
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND: //如果在主線(xiàn)程發(fā)送事件行施,則先將事件入隊(duì)列蕉堰,然后通過(guò)線(xiàn)程池依次處理事件;如果在子線(xiàn)程發(fā)送事件悲龟,則直接在發(fā)送事件的線(xiàn)程處理事件。
if (isMainThread) {
//[ 3.3 ] 將訂閱事件放入后臺(tái)隊(duì)列冰寻,依次調(diào)用執(zhí)行?
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC: //無(wú)論在那個(gè)線(xiàn)程發(fā)送事件须教,都將事件入隊(duì)列,然后通過(guò)線(xiàn)程池處理斩芭。
//? [ 3.4 ] 將訂閱事件放入線(xiàn)程池隊(duì)列轻腺,依此調(diào)用執(zhí)行訂閱者
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
- 根據(jù)訂閱事件的 threadMode類(lèi)型 進(jìn)行分發(fā)處理?
- 1.1. POSTING: 默認(rèn)的線(xiàn)程模式,在那個(gè)線(xiàn)程發(fā)送事件就在對(duì)應(yīng)線(xiàn)程處理事件划乖,避免了線(xiàn)程切換贬养,效率高
- 1.2 MAIN: 如在主線(xiàn)程(UI線(xiàn)程)發(fā)送事件,則直接在主線(xiàn)程處理事件琴庵;如果在子線(xiàn)程發(fā)送事件误算,則先將事件入隊(duì)列仰美,然后通過(guò) Handler 切換到主線(xiàn)程,依次處理事件儿礼。
- 1.3 MAIN_ORDERED: 無(wú)論在那個(gè)線(xiàn)程發(fā)送事件咖杂,都先將事件入隊(duì)列,然后通過(guò) Handler 切換到主線(xiàn)程蚊夫,依次處理事件
- 1.4 BACKGROUND: 如果在主線(xiàn)程發(fā)送事件诉字,則先將事件入隊(duì)列,然后通過(guò)線(xiàn)程池依次處理事件知纷;如果在子線(xiàn)程發(fā)送事件壤圃,則直接在發(fā)送事件的線(xiàn)程處理事件。
- 1.5 ASYNC: 無(wú)論在那個(gè)線(xiàn)程發(fā)送事件琅轧,都將事件入隊(duì)列伍绳,然后通過(guò)線(xiàn)程池處理。
- 對(duì)事件進(jìn)行分發(fā)后鹰晨,判斷決定將
Event事件
直接通過(guò)反射執(zhí)行 或 放入相應(yīng)隊(duì)列中逐個(gè)執(zhí)行墨叛。
postSticky粘性事件發(fā)布
通過(guò)對(duì)源碼的分析我們可以發(fā)現(xiàn)
postSticky發(fā)布過(guò)程
與post發(fā)布過(guò)程
的區(qū)別,我們看源碼:
public void postSticky(Object event) {
synchronized (stickyEvents) {
//[ 1 ] 將訂閱事件類(lèi)型class與事件關(guān)聯(lián)模蜡,放入Map集合緩存?
stickyEvents.put(event.getClass(), event);?
}
//將給定的事件發(fā)布到事件總線(xiàn)
post(event);
}
可以看出
postSticky函數(shù)
在執(zhí)行post函數(shù)
之前漠趁,執(zhí)行了一次對(duì)完整Event事件
的Map集合
存儲(chǔ),而Map集合的特性
決定了在Key值
相同的情況下,存儲(chǔ)的Value值
以最后一次放入的準(zhǔn)忍疾,這也正符合postSticky事件
的 同樣參數(shù)類(lèi)型闯传,以最后一次發(fā)布的事件為準(zhǔn) 特性
Post 、postSticky事件接收
Post事件接收
發(fā)送
POST事件
一定要確保接收方頁(yè)面執(zhí)行過(guò)了register注冊(cè)
,并且已存在對(duì)應(yīng)的接收方法卤妒,即 被訂閱完成后再執(zhí)行事件發(fā)布甥绿,方可被正確接收
postSticky事件接收
postSticky事件
在發(fā)布階段進(jìn)行了一次對(duì)完整Event事件的事件的緩存。
若接收方此時(shí)還未執(zhí)行register注冊(cè)
则披,則 Event事件則將一直存儲(chǔ)于內(nèi)存中共缕,直至接收方register注冊(cè)
完成后,將會(huì)內(nèi)存中獲取完整Event事件,當(dāng)做一個(gè)新Event事件發(fā)布到總線(xiàn)上士复,進(jìn)而被正確獲取图谷。
我們將register注冊(cè)
時(shí)對(duì)postSticky事件
特殊特殊處理源碼粘出供大家理解:
若接收方已執(zhí)行完register注冊(cè)
,則直接進(jìn)行發(fā)布
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
...
...?
//?判斷當(dāng)前
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();
// @重點(diǎn) 發(fā)布到事件總線(xiàn)
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
//根據(jù)訂閱方法類(lèi)型阱洪,從緩存集合獲取對(duì)應(yīng)的 粘性事件?
Object stickyEvent = stickyEvents.get(eventType);
// @重點(diǎn) 發(fā)布到事件總線(xiàn)
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
問(wèn)題回答
[ 1 ] Post事件
與 postSticky事件
的使用場(chǎng)景
[ 2 ] Post事件
與 postSticky事件
的區(qū)別
Post事件
必須在接收方完成register注冊(cè)
操作后發(fā)布才能正常被接收到盔粹,否則無(wú)法被接收隘梨;而postSticky事件
則沒(méi)有這樣的限制要求。Post
發(fā)布的事件若沒(méi)有被訂閱的事件會(huì)將發(fā)生流轉(zhuǎn)玻佩,即 作廢出嘹,無(wú)效;而postSticky
發(fā)布的事件未被訂閱咬崔,則將存儲(chǔ)于內(nèi)存中税稼,當(dāng)訂閱者完成register注冊(cè)
后從內(nèi)存中取出,當(dāng)做一個(gè)新事件進(jìn)行發(fā)布垮斯。
[ 3 ] postSticky事件
實(shí)現(xiàn) 粘性的原理
postSticky
的原理是:postSticky
在發(fā)布時(shí)將完整的事件存儲(chǔ)于全局Map集合 stickyEvents
中郎仆,而當(dāng)接收方進(jìn)行register注冊(cè)
是,會(huì)對(duì)粘性事件做特殊處理兜蠕,從stickyEvents
Map集合中取出相應(yīng)的事件扰肌,交由postToSubscription
函數(shù),進(jìn)行重新發(fā)布.