上一篇我們講了eventBus 的使用和註冊 EventBus使用及源碼解析之註冊
建議在看消息發送的時候先看上一篇,這篇講到的有些東西在上邊提過的我就不再多說了。
消息的發送有兩種方法,post和postSticky,其實postSticky 是對post的又一次包裝,不過是postSticky 多了一步操作,它將要發送的消息重新緩存到了stickyEvent中了,操作完成後還是調用了post 方法。
EventBus.getDefault().post("普通事件");
EventBus.getDefault().postSticky("粘性事件");
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);
}
我們在上邊註冊的時候有看到,在最後我們遍歷了stickyEvents map ,它的數據存儲入口就是這裏,並且它存完數據後又執行了post方法。
public void post(Object event) {
// PostingThreadState 是消息隊列工具類,並且是線程安全的,他是這個隊列封裝類變量的的控制器,且每個線程只有一個PostingThreadState 對象
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 循環,逐個發送任務
while (!eventQueue.isEmpty()) {
//開始發送,獲取第一個元素並刪除隊列中第一個元素,
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
//執行完成後將隊列初始化
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
postSingleEvent 是對單個任務的發送,
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//獲取發送任務消息的class
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
//獲取訂閱函數參數類型的class集合
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的三個參數分別是1.event消息實例 2.隊列控制封裝類 3.訂閱方法的參數類型
其中3的參數我們可以拿到訂閱方法的所有信息的封裝類 Subscription集合,上篇專門講過。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//subscriptionsByEventType 對象就是我們註冊時保存訂閱方法的對象,它的鍵時訂閱方法的參數的classe類型,每個classs類型歸位一類,他返回的事一個CopyOnWriteArrayList<Subscription> 集合。是訂閱方法參數類型一致的訂閱的方法的集合。
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
//Subscription 是單個訂閱方法的封裝類
for (Subscription subscription : subscriptions) {
//將消息內容及訂閱方法封裝類負值給postingState
postingState.event = event;
postingState.subscription = subscription;
boolean aborted;
try {
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
//執行完成後初始化postingState 後邊消息還會複用
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
//如果這時隊列類被標記爲取消,那麼就推出循環訂閱
if (aborted) {
break;
}
}
return true;
}
return false;
}
我們看到現在調用到了postToSubscription 方法,這個方法我們在上一篇中也講過,主要用於判斷執行在那個線程中。然後執行
invokeSubscriber 方法使用反射將消息傳遞給訂閱方法,同時訂閱方法被執行。
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);
}
}
最終執行
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);
}
}
然後我麼的訂閱方法執行
//訂閱事件,並且當前方法是在主線程中被調用的,sticky表示是否粘性事件
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void onEventTest(Object msg){
Log.d(TAG, "onEvent: " + msg.toString());
EventBus.getDefault().removeStickyEvent(msg);
}
如果你的事件是一個粘性訂閱事件,當收到的消息也是一個粘性消息時,如果後邊沒有其他需求,需要將該消息從粘性消息map 中移除,否則後邊如果有新註冊的粘性訂閱方法還會收到此消息。
總結:整個消息發送的過程實際就是消息重組和消息入列和出列的過程,在發送的時候無論是哪種發送方法,凡事已經註冊和並訂閱的方法都會被調用到,除非你手動設置了隊列的停止和刪除動作。