前言
本文主要從源碼的角度來分析事件總線 EventBus 的實現原理, EventBus 是常用的消息傳遞的方式之一,其他常見的消息傳遞的方式還包括 Handler
、BroadcastReceiver
、Listener
。通過本篇你在掌握 EventBus 基本使用的基礎之上,能夠掌握 EventBus 的實現原理。下面的框架圖可以清晰的看到這一點。
一、定義事件類
作為事件的發布者,需要定義所發布的事件的類:
public class MessageEvent {
private String msg;
public MessageEvent(String msg) {
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
二、注冊/取消注冊響應事件
作為事件的訂閱者,需要把響應事件的對象注冊到EventBus當中:EventBus.getDefault().register(obj)
當不需要處理某個類型的事件的時候,取消對這個事件的監聽:EventBus.getDefault().unregister(obj)
三、聲明和注釋訂閱方法,選擇指定線程模式
作為事件的訂閱者,需要定義事件的響應方法,方法名稱可以隨意取,方法的形參類型,必須和監聽的事件對象類型一致:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Toast.makeText(this, event.getMsg (),
Toast.LENGTH_SHORT).show();
}
3.1 四種線程模式
事件訂閱者可以通過注解的方式選擇處理事件的方法所在的線程:
PostThread: 如果事件處理函數指定了線程模型為PostThread,那么事件的發布和接收處理會在同一個線程當中。
BackgroundThread: 如果事件處理函數指定了線程模型為BackgroundThread,那么如果事件是在UI線程中發布出來的,那么該事件處理函數就會在新的子線程中運行,如果事件發布本來就是非UI線程中發布出來 的,那么該事件處理函數直接在發布事件的線程中執行。
MainThread: 如果事件處理函數指定了線程模型為MainThread,那么不論事件對象是在哪個線程中發布出來的,該事件處理函數都會在UI線程中執行。
Async: 如果事件處理函數指定了線程模型為Async,那么無論事件在哪個線程發布,該事件處理函數都會在新建的子線程中執行。
3.2 黏性事件
通過注解的方式設置sticky為true,那么事件處理函數則可以處理上一次的事件對象:
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
四、EventBus 3.0源碼詳解
4.1 注冊流程
/**
* Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
* are no longer interested in receiving events.
* <p/>
* Subscribers have event handling methods that must be annotated by {@link Subscribe}.
* The {@link Subscribe} annotation also allows configuration like {@link
* ThreadMode} and priority.
*/
public void register(Object subscriber) {
//通過注冊的對象得到其類的class對象
Class<?> subscriberClass = subscriber.getClass();
//通過類的class對象得到此對象的訂閱方法列表
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod :subscriberMethods) {
//線程同步,遍歷訂閱方法列表,注冊每一個訂閱方法
subscribe(subscriber, subscriberMethod);
}
}
}
代碼subscriberMethodFinder.findSubscriberMethods(subscriberClass)獲取訂閱方法列表具體如下:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//在緩存中查找此class對象對應的訂閱方法列表
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//是否忽略注解器生成的MyEventBusIndex類
if (ignoreGeneratedIndex) {
//通過反射機制得到訂閱者類class對象對應的訂閱事件方法列表
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//從注解器生成的MyEventBusIndex類中獲得訂閱類的訂閱方法列表
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//緩存此class對象的訂閱方法列表
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
通過反射機制獲取訂閱方法列表:
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//遍歷當前class對象和其父類中的訂閱方法
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
findUsingReflectionInSingleClass方法:
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");
}
}
}
至此,我們得到了所有的訂閱函數列表,下一步,會對每一個訂閱函數進行注冊:
// Must be called in synchronized block
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) {
// 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);
}
}
事件的注冊流程實際是從監聽者對象和消息事件兩個維度,將對方分別添加到自己對應的列表當中,具體可以通過以下流程圖總結:
4.2發布流程
/** Posts the given event to the event bus. */
public void post(Object event) {
//通過ThreadLocal機制得到當前線程的postingState對象
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
//在此線程的eventQueue中添加此事件對象
eventQueue.add(event);
if (!postingState.isPosting) {
//判斷當前線程是否UI線程
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;
}
}
}
ThreadLocal機制可以存儲各個線程的局部數據;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));
}
}
}
事件消息對象具體的分發函數:postToSubscription
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);
}
}
至此,我們完成了事件消息對象的分發流程,以下流程圖來總結post的過程:
4.3 取消注冊流程
/** 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) {
//遍歷事件類型列表,得到每個事件類型對應的訂閱者對象列表,遍歷這個列表,如果是此觀察者對象,則從列表中刪除
unsubscribeByEventType(subscriber, eventType);
}
//typesBySubscriber中刪除此訂閱者對象
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
unsubscribeByEventType函數:
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(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--;
}
}
}
}
取消注冊的流程總結如下:
通過觀察者類對象通過MAP表得到其對應的事件類class對象列表.
遍歷list列表,通過事件類class對象得到其在MAP表中對應的觀察者類對象列表。
遍歷此觀察者對象列表,判斷如果列表中存在需要取消的注冊觀察者對象,則從對象列表中刪除此觀察者對象。
從第1步中得到的MAP對象中刪除以取消注冊的觀察者對象為key的映射項目。
完成unregister過程。