首先通過subscriberMethodFinder通過類型信息獲取subscriber類中定義的onEvent***()方法列表,存儲在subscriverMethods中。具體見2.2.7。
2.3.1.2 subscribe()
之後,調用subscribe方法
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
Class<?> eventType = subscriberMethod.eventType;
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);//獲取註冊了消息類型(eventType)的註冊者
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
if (subscriptions == null) {//如果沒註冊該消息類型
subscriptions = new CopyOnWriteArrayList<Subscription>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {// 如果已經註冊,此種情況爲,針對同一個Subscriber對象(如Activity activity、Fragment fragment等)中調用了兩次register()方法,無論是否是重載的register()方法
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<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);//按消息類型添加subscriber
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<Class<?>>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (sticky) { //??? what meaning
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);
}
}
}
2.3.1.2.1 CopyOnWriteArrayList的add(int, E)方法
public synchronized void add(int index, E e) {
Object[] newElements = new Object[elements.length + 1];
System.arraycopy(elements, 0, newElements, 0, index);
newElements[index] = e;
System.arraycopy(elements, index, newElements, index + 1, elements.length - index);
elements = newElements;
}
add(E)方法
public synchronized boolean add(E e) {
Object[] newElements = new Object[elements.length + 1];
System.arraycopy(elements, 0, newElements, 0, elements.length);
newElements[elements.length] = e;
elements = newElements;
return true;
}
可以看到CopyOnWriteArrayList與ArrayList的區別,ArrayList是聲明一個定長數組,添加數據直接在數組裏添加,當數目超過數組長度時,再聲明一個更長的數組,將數據從舊數組copy到新數組裏。而CopyOnWriteArrayList是ArrayList的特例,它的數組總是被填滿的,每次添加數據時,都伴隨着新數組的創建,而且新數組長度比舊數組長度只多一,因此每次添加操作都伴隨着數據的複製。
2.3.1.2.2 sticky 參數
????
2.3.1.2.3
看下subscriptions.contains(newSubscription),其取決於ArrayList中的object是否equals給定的參數。Subscription類中重寫了equals()方法。@Override
public boolean equals(Object other) {
if (other instanceof Subscription) {
Subscription otherSubscription = (Subscription) other;
return subscriber == otherSubscription.subscriber && subscriberMethod.equals(otherSubscription.subscriberMethod);
} else {
return false;
}
}
因此如果針對相同的subscriber對象調用register()兩次,無論是否爲重載的,都會走到
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
。
2.3.2 unregister()方法
unregister()方法必須要調用,否則會造成內存泄露,具體原因見之前對typesBySubscriber的介紹。
/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
整體思路就是把不需要再接收消息的Subscriber對象和處理方法(subscription)從HashMap中清除掉。比較有意思的是unubscribeByEventType()裏for循環的寫法。
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unubscribeByEventType(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--;
}
}
}
}
首先,一個subsciptions列表中可以有多個subscription的subscriber與傳入的subscriber相同,這是由於對相同的事件類型,可以有不同的threadMode,即一個Subscriber中,可以有onEventMainThread(EventA eventa),也可以有onEventBackgroundThread(EventA eventa)、onEventAsync(EventA eventa)、onEvent(EventA eventa)。
其次,在對List做遍歷操作的同時更改List的內容,如果書寫不當,是會拋出Concurrent異常的。此處的寫法無問題,很值得借鑑。
2.4 post()與postSticky()
/** Posts the given event to the event bus. */
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get(); //獲取當前線程中postMessage的狀態
List<Object> eventQueue = postingState.eventQueue; //當前未完成的事件處理隊列
eventQueue.add(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;
}
}
}
2.4.1 currentPostingThreadState是EventBus對象內部的ThreadLocal類型的變量,雖然是一個對象,但在不同的線程中調用get()方法,會返回該線程的PostingThreadState變量,每個線程的PostingThreadState都指向不同的對象句柄。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
2.4.1.1 這裏用到了java的匿名內部類,實際上是創建一個繼承於ThreadLocal類的對象,完整寫法爲:
class ThreadLocalSub<T> extends ThreadLocal {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
}
private final ThreadLocalSub<PostingThreadState> currentPostingThreadState = new ThreadLocalSub<PostingThreadState>();
2.4.1.2 看下ThreadLocal的get()方法
/**
* Returns the value of this variable for the current thread. If an entry
* doesn't yet exist for this variable on this thread, this method will
* create an entry, populating the value with the result of
* {@link #initialValue()}.
*
* @return the current value of the variable for the calling thread.
*/
@SuppressWarnings("unchecked")
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
其中,Thread對象內部存有Values對象, 如果Values爲空,則創建一個,並賦值給Thread的Values對象;如果不爲空,則在values.table中尋找當前ThreadLocal對象的值(Thread可以有多個ThreadLocal變量),如果找到,則直接返回;如果未找到,則創建,實際上調用的是ThreadLocal的initialValue方法(具體可見values.getAfterMiss(this)方法)。另外這是一種很好的解耦方式。
2.4.1.3 PostingThreadState類存儲有當前線程中EventBus發送消息的相關信息,如事件隊列、當前是否主線程、當前發送的消息、消息類型、是否被取消、是否在發送。
/** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
這裏需要提個問題:爲什麼需要使用ThreadLocal變量,而不是一個全局的PostingThreadState對象?
答案是明顯的,如果使用一個全局的PostingThreadState變量,那麼多個線程中調用post()方法就必須進行同步操作,反覆獲取鎖、丟棄鎖,造成性能消耗;同時,post操作淪爲一個順序的操作,雖然可以多線程中調用,卻只能一個接一個的發送。而使用ThreadLocal變量,每個線程中自己進行狀態維護,可以並行執行,且不需要同步,性能消耗小。
但另一個問題來了,由於使用了ThreadLocal變量,所以post()代碼只需要考慮單個線程執行的情況,那麼使用isPosting變量進行鎖限制,就很多餘了。因爲一個線程裏,必然要執行完前一個方法,才執行下個方法。這是爲什麼呢?
2.4.2 post()方法主要調用了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) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
postSingleEvent()方法又主要調用了postSingleEventForEventType()方法。
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;
}
這裏有個問題,如果subscriptions是ArrayList類型,對subscriptions的遍歷不在synchronized語句塊裏,多線程情況下,應該有一定的概率會出現問題。
但是需要注意的是:subscriptions是CopyOnWriteArrayList類型的,它在java.util.cocurrent包中,在一個線程中對其遍歷,在另外線程中,對其進行更改,是經過同步處理的。
最終調用的是postToSubsciption方法,結合之前說的mainThreadPoster、backgroundPoster、asyncPoster,很好理解。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
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);
}
}
主要看下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);
}
}
三、 onEvent****(Object o)與onEvent****(具體的類型)比較,哪個效率更高?各有什麼好處?
onEvent***(Object o)這種寫法,在subscriptionsByEventType裏,以Object.class(key)和subscribers(value)爲鍵值對存儲。
在eventInheritance爲true時,post()任意EventType的消息,都會調用onEvent***(Object o)方法。而eventInheritance爲false時,只有post(Object o)時,纔會調用onEvent***(Object o)方法。與onEvent***(具體的類型)比較,自然是後者的效率高。EventBus的實現思路,其實就是通過類型信息來做事件區分的。