EventBus 2.4 原始碼分析
EventBus簡介
本篇基於EventBus 2.4撰寫。
Android optimized event bus that simplifies communication between Activities, Fragments, Threads, Services, etc. Less code, better quality.
上面是從官方repo拉來的程式碼,大致是說簡化的元件之間的交流通訊,減少程式碼,提高質量。
其實和EventBus最早是在qzone的程式碼裡認識的,空間內部有一個叫eventcenter的東西,曾經有優化過一些,當時看原始碼實現的時候發現的原來是根據EventBus改的一個實現。大概就是把annotation的實現改成了介面實現,另外去掉了根據Event型別來找訂閱者的模式,完全通過Event的TYPE型別常量來判斷,register的時候直接指定對哪種TYPE感興趣,輔助的判斷則有事件傳送者引用。這種實現見仁見智吧,雖然直接通過介面肯定是能提高效能的。這裡要吐槽的是實現修改的時候,直接把很多對外的介面名字改掉了,何必呢。
EventBus的好處是顯而易見的,完全解耦了請求鏈之間的關係,避免了請求者被長持有,又比廣播更輕量,比LocalBroadcast則更強大,介面也簡單實用。缺點的話,像是各種Event的定義是一個工作量。
原始碼分析 - 註冊(register)
EventBus.java
:
private synchronized void register(Object subscriber, boolean sticky, int priority) {
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
register的時候,大致就是去subscriber裡面首先找到那些onEvent方法(目前實現仍然是根據onEvent這個字首),尋找的時候會去判斷後綴,分為post執行緒、主執行緒、background執行緒,以及非同步執行緒,官方repo提到這裡之後在3.0可能會換成annotation的實現。
sticky引數是粘性事件概念,postSticky和registerSticky相對應,stickyEvent會記錄該EventType對應的最後一次postSticky的事件,這樣在registerSticky的時候,會立即檢查是否有之前post的事件,從而避免了某些事件去實現自己的快取。應用場景大概就是某些activity/fragment感興趣的事件發生在建立前,這樣則可以避免必須實現快取(當然事實上應用場景還是比較少的,因為大部分東西我們還是會在哪裡記錄一下)
SubscriberMethod.java
:
final class SubscriberMethod {
final Method method;
final ThreadMode threadMode;
final Class<?> eventType;
/** Used for efficient comparison */
String methodString;
}
SubscriberMethod裡面記錄了Method引用,執行緒模式(在findSubscriberMethods裡拿到的),eventType,以及用來提高method.equals效能的methodString。
接著再看subscribe方法的實現,在register最後,對找到的所有方法都去執行了一遍subscribe
EventBus.java
:
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
Class<?> eventType = subscriberMethod.eventType;
// CopyOnWriteArrayList就是個ImmutableArrayList, add/set等方法會返回一個新的ArrayList
// subscriptionsByEventType是一個hashmap,key是事件型別,value則是訂閱者陣列
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
// 該eventType在map裡還不存在,新建一下對應的subscription陣列,放進去map
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<Subscription>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
// 重複註冊的時候丟擲異常,這裡如果應用如果覺得無傷大雅其實可以直接return
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
// 根據優先順序去新增到對應的位置,高優先順序在前面也就會先處理
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
subscriptions.add(i, newSubscription);
break;
}
}
// typesBySubscriber是另一個map,顧名思義是以subscriber為key的一個map,被用在
// 1) isRegistered(Object subscriber)方法加速判斷是否已註冊,用空間換時間
// 2) unregister的時候直接可以拿到subscriber訂閱的所有eventType,然後去從map移除,避免需要遍歷所有eventType的map
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<Class<?>>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
// 粘性事件的話,就去立刻找一下是否有之前post過的事件,有則立即post給該subscriber
if (sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
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);
}
}
}
原始碼分析 - 傳送事件(post)
再來看一下對應的post邏輯
EventBus.java
:
/** Posts the given event to the event bus. */
public void post(Object event) {
// 獲得當前post執行緒的狀態,實現貼在下面了,currentPostingThreadState是ThreadLocal<PostingThreadState>變數,每個執行緒get和set的都是單獨的一份資料
PostingThreadState postingState = currentPostingThreadState.get();
// 往事件佇列裡面新增該event
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
// 如果當前不在posting事件
if (!postingState.isPosting) {
// 設定是否在主執行緒
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
// 設定當前正在post事件
postingState.isPosting = true;
// canceled狀態,丟擲異常
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
// 迴圈post從eventQueue裡面拿出來的event
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
// 置位回去
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
// 可以通過cancelEventDelivery去取消事件傳遞
boolean canceled;
}
// 單個事件的post處理
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
// 繼承鏈處理,比如Event本身的父類的subscriber也會收到,getDefault的時候預設為true。
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) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
// 沒想到還有這種邏輯吧,沒找到訂閱者的,則會發送一個NoSubscriberEvent出去
post(new NoSubscriberEvent(this, event));
}
}
}
// 對特定的event去post單個事件
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 {
// 結果實際post還在這個方法內實現
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
// 如果cancel了,則不再繼續傳遞事件
if (aborted) {
break;
}
}
return true;
}
return false;
}
// 具體的事件分發
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
// 這裡就是EventBus的一個很強大的功能了,根據訂閱者的訂閱方法監聽執行緒去處理
// 如果post和監聽方法在同一個執行緒則立即invoke對應方法
// 否則會去入佇列到對應執行緒handler進行處理
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
invokeSubscriber(subscription, event);
break;
case MainThread:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BackgroundThread:
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);
}
}
End
大致就講了一下register和post這一對比較常用的介面,其他還有一些實現像是EventBusBuilder,SubscriberException,cancelEventDelivery,AsyncExecutor就不在這裡進行贅述,之後可能會對AsyncExecutor單獨開一篇講一下,另外也會對otto的實現做一下分析。