EventBus原始碼解析(二)—釋出事件和登出流程
前言
上一篇部落格已經比較詳細的講解了EventBus的註冊過程,有了上一篇部落格的基礎,其實關於EventBus的原始碼中的其他流程就非常好理解了,尤其是我認為EventBus中最為重要的兩個Map,理解了兩張圖其實就理解了EventBus的原理。
原始碼分析
1.釋出事件
post方法
/** Posts the given event to the event bus. */
public void post(Object event) {
//ThreadLocal儲存的,不同的執行緒互相不干擾
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
// 將事件新增進當前執行緒的事件佇列
eventQueue.add(event);
if (!postingState.isPosting) {//如果當前執行緒正在傳送
postingState.isMainThread = isMainThread();
postingState.isPosting = true ;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
//迴圈從隊首取訊息,傳送訊息
postSingleEvent(eventQueue.remove(0 ), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
既然是釋出事件,肯定是post方法。
這裡首先要注意一點,這裡的currentPostingThreadState
是一個ThreadLocal型別,既然選擇TreadLocal型別,那麼特點當然就是執行緒單例,不同的執行緒之間互相不干擾,因為我們知道EventBus是支援多執行緒之間的事件傳遞的。
接下來的原始碼都比較簡單,將事件加入當前執行緒中用於儲存事件的佇列中,然後遍歷佇列中的事件,執行postSingleEvent
方法傳送事件。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
//找到類和它的父類
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
//直接傳送當前類
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
//沒有找到
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
可以看到這裡的流程比較簡單,基本都是最後呼叫了postSingleEventForEventType
方法,當然如果沒有找到,則會發送一條沒有找到的NoSubscriberEvent
.
這裡有個比較重要的引數eventInheritance
,這個如果我們沒有特殊配置的話,預設是true,我們來看一下這個引數是幹什麼的,可以看到當這個引數為true的時候,會呼叫lookupAllEventTypes
方法,返回一個List集合。
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
synchronized (eventTypesCache) {
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<>();
Class<?> clazz = eventClass;
while (clazz != null) {
//加入當前類
eventTypes.add(clazz);
//加入當前類的所有介面
addInterfaces(eventTypes, clazz.getInterfaces());
//加入當前的父類
clazz = clazz.getSuperclass();
}
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}
可以看到,這個方法的所用就是用一個List儲存該Event型別自己本身,和其繼承的所有父類,和實現的所有介面。
所以當得到這個List集合後,會遍歷這個集合,呼叫postSingleEventForEventType
傳送事件,所以這裡可以看出預設情況下不要隨意將Event繼承或者實現介面,當傳送該Event的時候,都會一併發出。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
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 {
//反射傳送事件
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;
}
這時就可以看到我們前一篇部落格提到的非常重要的一個Map:subscriptionsByEventType
,這裡放上這個Map的圖便於理解。
所以我們通過Event的型別,找到了所有訂閱該Event的資訊CopyOnWriteArrayList<Subscription>
。接下來就是遍歷這個List呼叫postToSubscription
方法,利用反射來呼叫訂閱的方法。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
//預設型別
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
//如果當前就在UI執行緒,則直接反射執行
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED:
//不同於MAIN,直接通過Handler的佇列執行,序列的
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) {
//如果當前是UI執行緒,則非同步
backgroundPoster.enqueue(subscription, event);
} else {
//不是UI執行緒,則在該執行緒執行
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
可以看到這裡實現了EventBus中的多執行緒功能,具體實現本篇部落格就不做講解了,後面如果有時間,會講解一下EventBus中的多執行緒。
可以看到最後其實都是呼叫invokeSubscriber
方法,從命名就能看出實現原理就是反射
void invokeSubscriber(Subscription subscription, Object event) {
try {
//通過反射執行事件
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
果然不出所料,最後就是通過我們經常看到的反射裡呼叫訂閱者中的訂閱方法,來實現EventBus中的事件流程。
2.登出流程
unregister
public synchronized void unregister(Object subscriber) {
//找到訂閱者訂閱的所有事件型別,也就是MainActivity中所有的Event
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
//到Event的map中刪除MainActivity
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
可以看到這裡又用到我們所說的重要的Map中的其中一個typesBySubscriber
,這裡再放一下這個Map的圖便與我們理解。
可以看到這裡,從typesBySubscriber
map中取出當前訂閱者的訂閱的所有事件集合List,然後再遍歷這個List,呼叫unsubscribeByEventType
方法進行取消訂閱,其實很容易聯想到這個方法肯定是再到另一個重要的Map中查詢訂閱了這個Event的訂閱者中移除當前Activity的訂閱者。
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
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);
//判斷兩個變數指向的是否是同一個記憶體地址
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
果然不出所料,又看到我們熟悉的另一個MapsubscriptionsByEventType
,這裡遍歷該Activity中訂閱的所有事件,然後通過Event到subscriptionsByEventType
中查詢訂閱了該Event的List<Subscription>
集合,然後找到當前這個訂閱者(注意這裡使用的是==,來判斷兩個Object物件是否相等,意味著是記憶體地址相同),找到後移除。至此登出流程也到此結束。
3.總結
通過本篇部落格,EventBus的基本流程基本上我們已經有了一定程度上的理解,詳細閱讀了這兩篇部落格後對於第一篇部落格中提出的問題也能相應理解。而且更重要的貫徹EventBus整個流程的兩個重要的Map。