EventBus for Android 源碼分析
上文執行個體講解EventBus for Android介紹了EventBus的基本用法,本文將介紹EventBus的實現原理。EventBus的實現主要圍繞兩個函數register和post,下面分別介紹之。
1 register(Object subscriber)
功能
註冊subscriber中以onEvent開頭的方法
代碼:
private synchronized void register(Object subscriber, boolean sticky, int priority) { //構造出方法列表 List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass()); for (SubscriberMethod subscriberMethod : subscriberMethods) { //註冊每個方法 subscribe(subscriber, subscriberMethod, sticky, priority); }}
重要資料結構:
SubscriberMethod:儲存方法、執行緒模式和Event事件處理的參數類型
subscriber函數用兩個Map:subscriptionByEventType和typesBySubscriber來維護事件訂閱者和事件類型的關係,如代碼所示
// Must be called in synchronized blockprivate void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) { Class eventType = subscriberMethod.eventType; CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType); Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority); if (subscriptions == null) { subscriptions = new CopyOnWriteArrayList(); subscriptionsByEventType.put(eventType, subscriptions); } else { if (subscriptions.contains(newSubscription)) { throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); } } // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again) // subscriberMethod.method.setAccessible(true); 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; } } List> subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null) { subscribedEvents = new ArrayList>(); typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); ...}
這個函數中用到了CopyOnWriteArrayList,下次研究一下。
2 post(Object event)
先看源碼:
public void post(Object event) { //postingState是一個ThreadLocal型變數, 表示發送事件的進程狀態 PostingThreadState postingState = currentPostingThreadState.get(); //將事件加如到隊列中 List eventQueue = postingState.eventQueue; eventQueue.add(event); //發送Event if (!postingState.isPosting) { postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper(); 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; } }}
這裡遇到一個類:PostingThreadState,用來記錄當前線程的狀態和任務隊列,EventBus用postSingleEvent來處理任務隊列中的每個任務,代碼如下:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { Class eventClass = event.getClass(); boolean subscriptionFound = false; if (eventInheritance) { List> 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) { post(new NoSubscriberEvent(this, event)); } }}
這個函數的主要功能是擷取Event的類型,如果eventInheritance為True,則會擷取Event的父類,和介面,然後對每一種類型調用一次 postSingleEventForEventType(Object event, PostingThreadState postingState, Class
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class eventClass) { CopyOnWriteArrayList subscriptions; synchronized (this) { //擷取相應事件類型為eventClass的訂閱者 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;}
在擷取到訂閱者,方法和發送事件的線程類型之後,EventBus調用 postToSubscription(Subscription subscription, Object event, boolean isMainThread)來通知訂閱者進行事件處理,該函數代碼如下:
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { switch (subscription.subscriberMethod.threadMode) { case PostThread: //直接調用Event處理事件 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); } }
該函數根據不同的執行緒模式來採用不同的方法來通知訂閱者,下面分別討論之:
1 PostThread模型
直接調用invokeSubscriber(Subscription subscription, Object event),該方法利用反射原理來調用接受者的事件處理方法
2 MainThread模型
若發送線程為主線程,則直接調用訂閱者事件處理方法。
若發送線程為非主線程,則將事件發送到mainThreadPoster中進行處理。mainThreadPoster的類型為HandlerPoster,繼承自Handler。HandlerPoster內部維護一個隊列(PendingPostQueue)來存取訂閱者和對應的事件回應程式法。每次往HandlerPoster中插入資料時, HandlerPoster便發一個訊息,告知Handler來從PendingPostQueue中取出資料,然後調用訂閱者的事件處理方法,代碼如下:
void enqueue(Subscription subscription, Object event) { PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); synchronized (this) { queue.enqueue(pendingPost); if (!handlerActive) { handlerActive = true; //若looper不在運行,則發訊息通知它 if (!sendMessage(obtainMessage())) { throw new EventBusException("Could not send handler message"); } } }}@Overridepublic void handleMessage(Message msg) { boolean rescheduled = false; try { long started = SystemClock.uptimeMillis(); while (true) { PendingPost pendingPost = queue.poll(); if (pendingPost == null) { synchronized (this) { // Check again, this time in synchronized pendingPost = queue.poll(); if (pendingPost == null) { handlerActive = false; return; } } } //取出隊列中的資料,調用訂閱者的方法 eventBus.invokeSubscriber(pendingPost); long timeInMethod = SystemClock.uptimeMillis() - started; if (timeInMethod >= maxMillisInsideHandleMessage) { if (!sendMessage(obtainMessage())) { throw new EventBusException("Could not send handler message"); } rescheduled = true; return; } } } finally { handlerActive = rescheduled; }}
3 BackgroundThread模型
若發送線程為非主線程,則直接呼叫事件響應函數。
若發送線程為主線程,則將訊息發送到backgroundPoster中。backgroundPoster的類型為BackgroundPoster,繼承自Runnable。和HandlerPoster類型,該對象也維護一個事件隊列:PendingPosteQueue,每次插入資料時,BackgroundPoster會將自己放入到EventBus的線程池中等待調度,完整代碼如下:
public void enqueue(Subscription subscription, Object event) { PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); synchronized (this) { queue.enqueue(pendingPost); //若線程池中的任務已經運行,則立即返回, if (!executorRunning) { executorRunning = true; //擷取到EventBus的線程池 eventBus.getExecutorService().execute(this); } }}@Overridepublic void run() { try { try { while (true) { //等待一秒後取出任務 PendingPost pendingPost = queue.poll(1000); if (pendingPost == null) { synchronized (this) { // Check again, this time in synchronized pendingPost = queue.poll(); if (pendingPost == null) { executorRunning = false; return; } } } eventBus.invokeSubscriber(pendingPost); } } catch (InterruptedException e) { Log.w("Event", Thread.currentThread().getName() + " was interruppted", e); } } finally { executorRunning = false; }}
4 Async模型
該模型直接將事件發送到asyncPoster中進行處理。asyncPoster的類型為AsyncPoster,繼承自Runnable。與BackgroundPoster不同的是,AsyncPoster將資料插入隊列後,直接將自己放入線程池中處理,完成代碼如下:
public void enqueue(Subscription subscription, Object event) { PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); queue.enqueue(pendingPost); eventBus.getExecutorService().execute(this);}@Overridepublic void run() { PendingPost pendingPost = queue.poll(); if(pendingPost == null) { throw new IllegalStateException("No pending post available"); } eventBus.invokeSubscriber(pendingPost);}
Async與BackgroundPoster的不同之處為前者每次加入訂閱者和事件後,便立即將自己放進線程池中,確保每一個任務都處於不同的線程中。而BackgroundPoster會根據線程池中的BackgroundPoster類型的任務是否處於運行狀態來往線程池中加任務,這樣就確保所有處於BackgroundThread模型中的事件處理任務都在同一個後台線程中運行。