EventBus其實(shí)解決了這樣幾個問題:保存訂閱者、發(fā)布事件、切換線程价捧、粘性事件
保存訂閱者
在register(this)時,處理運(yùn)行時注解涡戳,根據(jù)this找到類和類中的方法结蟋,根據(jù)方法的Subscribe注解找到訂閱事件的那些方法。
從Subscribe注解中找到指定的線程渔彰、優(yōu)先級椎眯、粘滯等;
從方法參數(shù)中找到訂閱的事件類eventType胳岂;
生成訂閱者Subscription對象,內(nèi)有訂閱的類和方法的引用
然后用eventType做key舔稀,把訂閱者存入concurrentHashMap中乳丰,因為一個eventType可能有多個訂閱者,而且是典型讀多寫少的場景内贮,所以用CopyOnWriteArrayList來保存這些訂閱者产园。
發(fā)布事件
在post時,根據(jù)eventType夜郁,從concurrentHashMap中尋找訂閱者什燕。
會根據(jù)父類查找,eventType是根據(jù)對象的類和父類一起判斷的竞端,所以會根據(jù)一個類的列表去查詢訂閱者屎即。
會排隊處理,為了確保一個接一個地發(fā)送事件事富,post其實(shí)是先進(jìn)入隊列Queue技俐,然后每次從隊列中彈一個event來處理的,這個隊列是個普通的List统台,實(shí)際上作為FIFO處理雕擂。
切換線程
同一個事件的不同訂閱者,可能需要不同的線程去執(zhí)行贱勃。
在找到訂閱者處理時井赌,根據(jù)訂閱者要求的線程模式谤逼,做個switch處理:
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(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);
}
可以看到,有默認(rèn)仇穗、mainThreadPoster流部、backgroundPoster、asyncHandler四類仪缸。
默認(rèn)會在當(dāng)前線程直接invokeSubscriber贵涵,傳遞事件給訂閱者。
mainThreadPoster其實(shí)就是一個持有主線程Looper的handler恰画,利用handler消息機(jī)制宾茂,實(shí)現(xiàn)在主線程的handleMessage函數(shù)里調(diào)用eventbus的invokeSubscriber。
backgroundPoster和asyncPoster都是Runnable拴还,都是獲取eventbus中的ExecutorService來處理跨晴,不過
asyncPoster對于隊列沒做什么特殊處理,每個事件都會一個線程片林。
而backgroundPoster做了入隊列的鎖同步端盆,在出隊列處理時,還可以等待1000毫秒:
PendingPost pendingPost = queue.poll(1000);
其實(shí)就是說费封,backgroundPoster會盡量復(fù)用線程焕妙。
粘滯消息
一般事件是訂閱在先,接收在后弓摘,但是有些情況下焚鹊,事件已經(jīng)發(fā)送,再去訂閱時還希望能拿到這個事件韧献,這時候就需要粘性事件末患。
粘性事件的注冊也是在注解中:
@Subscribe(sticky = true)
而粘性事件的發(fā)送也有專門的函數(shù):
EventBus.getDefault().postSticky(new MessageEvent("xxx"));
EventBus.getDefault().getStickyEvent(MessageEvent.class);
EventBus.getDefault().removeStickyEvent(stickyEvent);
粘性事件的實(shí)現(xiàn)原理,也是EventBus維持了一個concurrentHashMap類型的stickyEvents锤窑,這個Map的key是event.Class璧针,value則是event本身。
粘性事件的發(fā)送有兩個入口:
一是postSticky渊啰,每次都會先更新stickyEvents中的數(shù)據(jù)探橱,然后post這個event。
二是register虽抄,相當(dāng)于補(bǔ)發(fā)事件走搁,實(shí)現(xiàn)粘滯效果,具體是在每次register時迈窟,會查詢stickyEvent集合中私植,有沒有粘性事件,如果有车酣,就補(bǔ)發(fā)曲稼。