學習EventBus源碼

前言

因為平時比較喜歡用Eventbus進行跨組件通信,就比較好奇他是怎麼做到的,所以就開始學習Eventbus的源碼來了解Eventbus是怎麼跨組件的

開始分析源碼

獲取實例

按照我們平時操作Eventbus的流程進行分析,我們一般都是用

EventBus.getDefault()

去獲取實例,後面再進行其他的操作,所以我們先查看EventBus的getDefault()方法

public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}

用的是單例模式中的DCL模式,下面再來看構造函數EventBus()做了啥

public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}

this即調用了另外的一個帶參構造函數,傳入的是一個EventBusBuilder,然後Builder對EventBus進行了一些初始化的工作,可以明顯看出用的是建造者模式

所以我們可以想到應該可以手動傳參進行建造,即是

EventBus.builder().......build()

在兩個方法之間進行參數的手動設置,這樣就可以得到一個私人定製的EventBus實例了

平時在我們獲得EventBus的實例之後,我們便可以進行register和post兩個操作了

訂閱

我們先來看register方法,訂閱者必須調用register方法

public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}

傳入的參數就是訂閱者對象,然後我們可以看到兩個比較關鍵的就是SubscriberMethod和findSubscriberMethods,下面我們看一下他們內部幹了什麼

public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
this.method = method;
this.threadMode = threadMode;
this.eventType = eventType;
this.priority = priority;
this.sticky = sticky;
}

我們可以看到SubscriberMethod類中存了訂閱方法的各種屬性,分別是線程模式、事件類型、方法對象、優先順序、是不是粘性等

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}

if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}

首先我們看到程序是先到map緩存裡面找,看這個訂閱者(即這個activity之類的)是否已經建立了訂閱方法集合,如何已經有了就返回。如果不存在訂閱方法集合,就開始判斷ignoreGeneratedIndex參數,這個ignoreGeneratedIndex參數的作用可以從findUsingReflection(subscriberClass)和findUsingInfo(subscriberClass)兩個方法的不同看出就是是否忽略MyEventBusIndex,MyEventBusIndex是Eventbus3.0出的功能(不細說了),然後ignoreGeneratedlndex可以通過ignoreGeneratedlndex方法設置(也就是上面的自定義建造器),默認的是false,所以我們看一下 findUsingInfo(subscriberClass)

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}

中期一段涉及到subscriberInfo的操作就是直接使用subscriberInfo中的getSubscriberMethods()方法去獲得訂閱方法的集合(由此我們也可以看出如果使用MyEventBusIndex會讓效率提高),而FindState應該是一個整理類,他把方法集合收入進來進行一些分類,方便後續的調用,其實更主要是為方便使用MyEventBusIndex直接獲取方法集合,如果沒有使用MyEventBusIndex的話,這個方法和ignoreGeneratedIndex為true時的方法完全一致,即調用findUsingReflectionInSingleClass(findState)方法去獲取訂閱方法信息

private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length);
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}

程序先將類中所有的方法通過反射獲取,獲取方法的方式就是通過@Subscribe註解去找到的(get了!),之後就是獲取方法的各種信息新建一個方法對象再放入findState,然後下面就是兩個錯誤用法,此時findUsingReflectionInSingleClass(FindState findState)方法就完成了,這裡面完成了註冊最重要的通過註解獲取註冊者的所有訂閱方法,然後我們回到findUsingInfo,我們就會看到接下來就是回收findState並且放回方法集合,此時findUsingInfo方法就完成了,我們回到findSubscriberMethods中,然後如果方法集合為空就拋異常「沒有使用@Subscribe註解的公共方法」,然後存入緩存,此時我們終於回到了register方法中,發現它遍歷所有的訂閱方法傳入subscribe方法中,所以我們再來看一下subscribe方法(長代碼警告)

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
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);
}
}

int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}

List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);

if (subscriberMethod.sticky) {
if (eventInheritance) {
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);
}
}
}

一開始先把訂閱者和訂閱方法合起來創建一個訂閱對象,之後再根據事件種類獲取到訂閱對象集合,如果沒有集合便創建並存入,如果裡面已經包含了這個訂閱對象便拋異常,然後就是根據訂閱方法的優先順序存入訂閱對象集合中。接著就是通過訂閱者去獲取訂閱的訂閱方法類型集合,如果為null便創建,後面是將該事件類型存入訂閱者的事件類型集合中。緊接著就是對粘性事件的處理,(eventInheritance參數是是否向上查找事件的父類後面post中會提到),如果是粘性事件的話,對stickyEvents進行一個遍歷,即粘性事件集合遍歷,找到與當前訂閱方法 事件類型相同的,便執行 checkPostStickyEventToSubscription(newSubscription, stickyEvent)方法,此方法內部就是執行了postToSubscription方法,即後面要說的post篇,粘性事件處理邏輯我們也可以理解,就是如果訂閱者訂閱晚於事件發布,那麼當我們訂閱時便去調用StickEvents,將裡面沒有處理的粘性事件進行同類型post處理,此時便還是相當於方法訂閱在post之前,此時subscribe(Object subscriber, SubscriberMethod subscriberMethod)便說完了,這個方法中做了三件事

  1. 根據訂閱方法的優先順序存入訂閱對象的集合中
  2. 把該訂閱方法的事件類型存入訂閱者的事件類型中
  3. 對粘性事件進行處理

到此register就說完了

發送

我們現在進入post方法中

public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
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;
}
}
}

我們首先看到的是一個叫PostingThreadState的類

final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}

我們可以看到裡面保存了事件隊列、事件、是否是主線程等等信息,這樣要比使用Threadlocal快得多,緊接著就是取出事件隊列將post事件存入事件隊列中,接著就是對是否正在post參數、當前線程的更改和是否已經被中止的判斷等等,最重要就是來到postSingleEvent(eventQueue.remove(0), postingState)方法,真正開始處理post事件

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));
}
}
}

我們在這裡再次看到了eventInheritance參數(之前粘性事件中也提到了),這個參數表示是否向上查找父類,默認為true,當然也可以自定義builder來進行更改,有無這個參數就是是否連同post事件的父類的事件也一同觸發,這裡的lookupAllEventTypes(eventClass)方法就是為了獲取所有父類事件,接著就是遍歷所有事件類型並調用

postSingleEventForEventType(event, postingState, clazz)方法
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;
}

我們可以看到程序先通過事件類型去獲取訂閱對象集合,遍歷集合更改postingState參數,然後再執行postToSubscription(subscription, event, postingState.isMainThread)方法

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 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);
}
}

這個方法就是將事件傳遞給訂閱對象了,然後就是判斷當前線程和需求線程是否一致了,如果不一致就調用方法進行線程的切換,然後我們把每個線程切換方法打開,發現切換到主線程是繼承的handler,通過handler將線程切換到主線程,切換到子線程是繼承的Runnable,通過runnable切換到子線程,但是我們還是不知道他是怎麼讓訂閱者觸發訂閱方法的,此時讓我們看到void invokeSubscriber(Subscription subscription, Object event)方法

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);
}
}

原來是他!我們發現最後調用了Method.invoke方法動態運行訂閱者的訂閱方法。

至此我覺得EventBus比較常用的register和post都講完了,至於unregiser我們不貼出源碼也知道只要把剛才存入訂閱者的所有訂閱對象和訂閱方法清除就行了,因為這樣post就找不到了。

整個EventBus總結來說就是使用反射來達到跨組件通信的,這次學習源碼收穫很多,比如同步的使用重要性、反射的使用、組件的本質、CopyOnWriteArrayList使用意義等等,不得不說閱讀源碼有時候真的很快樂,收穫也很多


推薦閱讀:

TAG:Android | Android開發 |