簡介
本周我將開啟一個(gè)系列篇豆瘫,圍繞著EventBus的 運(yùn)行流程 + 源碼 的形式進(jìn)行精細(xì)剖析吞鸭,分為一下幾篇
EventBus系列『一』——注冊(cè)與注銷
EventBus系列『二』——Post與postSticky事件的發(fā)布與接收
EventBus系列『番外』——認(rèn)真剖析 『PendingPostQueue』隊(duì)列的實(shí)現(xiàn)思想
EventBus準(zhǔn)備提要
在講解我們注冊(cè)和注銷之前EventBus需要做一些準(zhǔn)備工作
[1] 將一些特殊重要屬性封裝成 ,PostingThreadState
類型寺董,并將PostingThreadState
的實(shí)例放置于ThreadLocal
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<>(); //訂閱事件隊(duì)列
boolean isPosting; //是否已發(fā)布
boolean isMainThread; //是否在主線程中發(fā)布的
Subscription subscription; //訂閱信息
Object event; //訂閱事件
boolean canceled; //是否已取消
}
[2] 準(zhǔn)備一個(gè)Map集合將訂閱事件參數(shù)類與事件參數(shù)的所有父類集合關(guān)聯(lián)起來
private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();
[3]創(chuàng)建一個(gè)EventBus
實(shí)例,初始化里面的參數(shù)
//創(chuàng)建單例
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
//創(chuàng)建EventBus的Log打印
logger = builder.getLogger();
//用于將訂閱方法的EventType屬性與訂閱事件集合
//(CopyOnWriteArrayList<Subscription>)關(guān)聯(lián)刻剥,緩存進(jìn)HashMap
subscriptionsByEventType = new HashMap<>();
////用于將注冊(cè)類與注冊(cè)類中的訂閱方法的類型集合(List<Class<?>>)關(guān)聯(lián)遮咖,緩存進(jìn)HashMap
typesBySubscriber = new HashMap<>();
// //用于緩存粘性事件,將粘性事件的消息實(shí)體類 與 粘性事件關(guān)聯(lián) 存入 ConcurrentHashMap中
stickyEvents = new ConcurrentHashMap<>();
//創(chuàng)建EventBus主線程實(shí)例
mainThreadSupport = builder.getMainThreadSupport();
//創(chuàng)建主線程Poster造虏,用于將事件放入主線程處理隊(duì)列
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
//創(chuàng)建 后臺(tái)的Poster 御吞,用于將事件放入后臺(tái)處理隊(duì)列
backgroundPoster = new BackgroundPoster(this);
//創(chuàng)建異步Poster ,用于將事件放入異步處理隊(duì)列
asyncPoster = new AsyncPoster(this);
//獲取訂閱事件注冊(cè)類的索引數(shù)量
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
//創(chuàng)建用于查詢訂閱事件的實(shí)例
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent =
builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
//獲取線程池實(shí)例
executorService = builder.executorService;
}
EventBus注冊(cè)
我們先來看一下
EventBus
注冊(cè)流程圖:
我將歷程圖分為兩個(gè)部分 第一部分為【主流程】而 第二部分為【事件集合的遍歷流程】 以方便大家理解漓藕,那么我們接下來就依據(jù)流程圖為大家逐一講解.
[1] EventBus.getDefault()
獲取EventBus
實(shí)例
[2] 進(jìn)入 EventBus.java
#方法 EventBus.getDefault().register(this)
函數(shù)
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass(); //獲取 subscriber類對(duì)象 即 上層參數(shù) [ this ] 的類對(duì)象
// [ 2.1 ] 檢索出注冊(cè)類中的所有? [ 訂閱信息方法]
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass)陶珠;
//由于訂閱事件必須在同步塊中實(shí)現(xiàn),所以這里使用synchroniezd關(guān)鍵字鎖住??
synchronized (this) {
//使用for循環(huán),將事件逐個(gè)發(fā)布?
for (SubscriberMethod subscriberMethod : subscriberMethods) {
// [2.2] 發(fā)布訂閱信息
subscribe(subscriber, subscriberMethod);
}
}
}?
- 檢索出注冊(cè)類中的所有? [ 訂閱信息方法]
- 遍歷獲取的訂閱信息集合享钞,并逐一發(fā)布訂閱信息
[2.1] 檢索注冊(cè)類中的所有訂閱方法 進(jìn)入 SubscriberMethodFinder.java
執(zhí)行
subscriberMethodFinder.findSubscriberMethods(subscriberClass)
//全局 [ 訂閱事件 ] 方法緩存Map
?private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
?List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//根據(jù)注冊(cè)類class,從緩存中獲取訂閱事件鏈?
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
? //已緩存過揍诽,直接返回訂閱事件List
if (subscriberMethods != null) {
return subscriberMethods;
}
//?是否使用通過反射生成的索引,默認(rèn)false
if (ignoreGeneratedIndex) {
//通過索引類取出注冊(cè)類的訂閱事件信息 @問題:注冊(cè)時(shí)如何通過索引類獲取注冊(cè)類的訂閱事件集合
subscriberMethods = findUsingReflection(subscriberClass);
} else {?
? //使用反射獲取訂閱事件鏈List? @問題:注冊(cè)時(shí)如何通過反射獲取注冊(cè)類的訂閱事件集合
subscriberMethods = findUsingInfo(subscriberClass);
}
//如果獲取的事件鏈List為空嫩与,則拋出異常?
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//將注冊(cè)類與事件鏈List關(guān)聯(lián)起來寝姿,放入緩存?
METHOD_CACHE.put(subscriberClass, subscriberMethods);
//返回訂閱鏈list?
return subscriberMethods;
}
}
- 根據(jù)注冊(cè)類class,從緩存中獲取訂閱事件鏈?,若獲取的訂閱信息方法集合已存在,則直接返回該集合划滋。
- 是否使用通過反射生成的索引饵筑,默認(rèn)false。
- 若使用反射生成索引 則通過索引類取出注冊(cè)類的訂閱事件信息处坪,否則通過反射獲取訂閱信息集合
- 將注冊(cè)類與事件鏈List關(guān)聯(lián)起來根资,放入緩存?
[2.2] 在 函數(shù) subscribe(subscriber, subscriberMethod)
中發(fā)布訂閱事件
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType; //獲取事件的類型
Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //生成訂閱實(shí)例
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); //根據(jù)事件類型架专,從Map緩存中查找訂閱鏈表
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions); //保存入Map中
} else {
if (subscriptions.contains(newSubscription)) { //在訂閱鏈表中匹配當(dāng)前訂閱事件
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType); //拋出一個(gè)EventBusException異常,告訴用戶該事件已經(jīng)被訂閱過了
}
}
? //將當(dāng)前的訂閱事件放入 subscriptions
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
//通過注冊(cè)類從typesBySubscriber中獲取?獲取對(duì)應(yīng)的subscribedEvents
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
//將注冊(cè)類和注冊(cè)類中的訂閱方法的evenType屬性集合關(guān)聯(lián)起來放入? typesBySubscriber 中
typesBySubscriber.put(subscriber, subscribedEvents);
}
//將訂閱方法?的eventType屬性放入subscribedEvents集合
subscribedEvents.add(eventType);
//判斷當(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();
//檢查粘性訂閱事件?
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
- 首先根據(jù)傳遞的訂閱信息和完整的訂閱方法集合,生成訂閱實(shí)例
- 根據(jù)事件類型玄帕,從
subscriptionsByEventType
Map緩存中查找對(duì)應(yīng)的訂閱鏈表- 若獲取的訂閱鏈表為空部脚,則將當(dāng)前訂閱方法的
eventType
屬性與空的CopyOnWriteArrayList
關(guān)聯(lián)起來放入subscriptionsByEventType
Map集合,之后我們會(huì)向CopyOnWriteArrayList
的實(shí)例對(duì)象subscribedEvents
填充數(shù)據(jù)裤纹,保證他們之間關(guān)聯(lián)準(zhǔn)確性- 將當(dāng)前的訂閱事件放入
subscriptions
- 通過注冊(cè)類從
typesBySubscriber
中獲取?獲取對(duì)應(yīng)的subscribedEvents
- 將注冊(cè)類和注冊(cè)類中的訂閱方法的
evenType
屬性集合關(guān)聯(lián)起來放入?typesBySubscriber
中- 將訂閱方法?的eventType屬性放入subscribedEvents集合
- 判斷當(dāng)前訂閱事件方法是不是粘性事件?,若是則 從緩存
stickyEvents
獲取委刘,并將其當(dāng)做一個(gè)新事件發(fā)布到總線
EventBus注銷
EventBus
注銷流程圖 :
與注冊(cè)相比注銷流程就顯得簡單的多了,主要的就是將事件及注冊(cè)類的移除鹰椒,接下來我們結(jié)合源碼進(jìn)行了解一下
[1] EventBus.getDefault()
獲取EventBus
實(shí)例
[2] 進(jìn)入 EventBus.java
#方法 EventBus.getDefault().unregister(this)
函數(shù)
public synchronized void unregister(Object subscriber) {
//通過注冊(cè)類從typesBySubscriber中獲取對(duì)應(yīng)的訂閱事件類型集合
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {?
for (Class<?> eventType : subscribedTypes) {
// [ 2.1 ] 移除與當(dāng)前注冊(cè)類相同的對(duì)象中包含的訂閱事件
unsubscribeByEventType(subscriber, eventType);
}
//從?typesBySubscriber中移除注冊(cè)類
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
- 通過注冊(cè)類從typesBySubscriber中獲取對(duì)應(yīng)的訂閱事件類型集合
- 遍歷集合移除與當(dāng)前注冊(cè)類相同的對(duì)象中包含的訂閱事件
- 遍歷結(jié)束后 即代表注冊(cè)類中的所有的訂閱都已經(jīng)被移除后從?
typesBySubscriber
中移除注冊(cè)類
[ 2.1 ] 移除與當(dāng)前注冊(cè)類相同的對(duì)象中包含的訂閱事件
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
//通過eventType從?subscriptionsByEventType獲取訂閱事件集合
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
//判定當(dāng)前訂閱事件的注冊(cè)類是否與要取消的注冊(cè)類對(duì)象相同?
if (subscription.subscriber == subscriber) {
//從訂閱集合中移除?
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
- 通過
eventType
從?subscriptionsByEventType
獲取訂閱事件集合- 遍歷集合锡移,判定當(dāng)前訂閱事件的注冊(cè)類是否與要取消的注冊(cè)類對(duì)象相同,若判定成功則將訂閱事件從集合中移除漆际。
總結(jié)
本篇我們?cè)敿?xì)講解了EventBus
的 注冊(cè) 與 注銷流程淆珊,從中可以發(fā)現(xiàn) :
- 在注銷流程其實(shí)沒什么復(fù)雜操作,就是講訂閱事件和注冊(cè)類進(jìn)行了逐一移除.
- 注冊(cè)流程就相對(duì)復(fù)雜許多奸汇,在注冊(cè)的最后我們發(fā)現(xiàn)了對(duì)
postSticky 粘性事件
的特殊處理施符,而并沒有對(duì)POST 事件
做什么特殊處理。