在《Android EventBus使用》博文中,我們已經對EventBus的使用進行了分析,在這篇博文中,我們就抽絲剝繭,對EventBus源碼進行分析。
基于Android EventBus 3.0.0版本進行分析
1. 觀察者注冊
我們知道事件的注冊是這樣的:
EventBus.getDefault().register(this);
這一行代碼其實包含了兩個操作:
- 獲取EventBus實例
- 注冊訂閱者
1.1 獲取EventBus實例
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
可見EventBus的設計使用了設計模式中的[單例模式]以及[構建者模式],并采用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;
}
這是什么鬼?構造函數竟然是public的,那開發者為何這樣設計呢?原因有如下兩點:
一方面,我們通過getDefault方法獲取到的是永遠是同一個對象,而在項目或模塊中使用同一個對象,有利于管理訂閱者和發布者;
另一方面,把構造的主動權交給使用者,方便使用者根據自己的需求和設計定制EventBus對象(通過Builder模式構造)。
1.2 注冊
明白了EventBus的構造,我們從注冊(register)開始來分析其源碼。
EventBus.register
//注冊訂閱者來接收事件。
//訂閱者必須調用 unregister方法,一旦他們決定不再接收事件。
//訂閱者接收事件的方法必須使用Subscribe注解來表示。
public void register(Object subscriber) {
//獲取訂閱者的class
Class<?> subscriberClass = subscriber.getClass();
//獲取訂閱者所有的事件處理方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//訂閱事件
subscribe(subscriber, subscriberMethod);
}
}
}
SubscriberMethod
在進行解析前,我們先來了解下SubscriberMethod是什么?
public class SubscriberMethod {
// 方法
final Method method;
// 方法運行模式
final ThreadMode threadMode;
// 方法事件類型
final Class<?> eventType;
// 方法優先級
final int priority;
// 是否接收黏性事件
final boolean sticky;
// Used for efficient comparison
String methodString;
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就是一個訂閱方法的實體類,記錄了訂閱方法的一些信息。
回到register方法,我們接著來分析如何遍歷查找訂閱者中所有的訂閱方法。
SubscriberMethodFinder.findSubscriberMethods
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//首先從緩存中讀取,如果緩存中已經存在則直接返回
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//是否忽略注解生成的編譯類
//該值默認為false,所以默認會采取編譯時注解實現
if (ignoreGeneratedIndex) {
//使用反射獲取訂閱方法
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//索引方式
//如果未配置,那最終還是將調用SubscriberMethodFinder.findUsingReflectionInSingleClass方法
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;
}
}
通過拋出的異常我們知道,訂閱方法應該定義為public.
findSubscriberMethods方法就是用來獲取訂閱者中所有的訂閱方法,并根據外部配置決定獲取訂閱方法的方式(通過生成的編譯類、通過反射),最后將獲取到的訂閱方法放入到緩存中備用。
總的來講,該方法還是很簡單明了的。
SubscriberMethodFinder.findUsingReflection
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
FindState其實就是一個保存了訂閱者和訂閱方法信息的一個實體類,包括訂閱類中所有訂閱的事件類型和所有的訂閱方法等。
SubscriberMethodFinder.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
//獲取類中聲明的所有public方法
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
//過濾方法:訂閱方法必須為public、非抽象、非static方法,必須只能有一個參數
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注解的方法,Subscribe Annotation才不為空
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
//獲取注解方法中的參數類型
Class<?> eventType = parameterTypes[0];
//檢查是否添加方法
if (findState.checkAdd(method, eventType)) {
//獲取注解上的threadMode的值
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注解、非抽象、非static、只有一個參數)。
值得注意的是:如果子類與父類中同時存在了相同訂閱方法,則父類中的訂閱方法不會被添加到subscriberMethods。
EventBus.subscribe
在找到訂閱方法后,會遍歷找到的所有訂閱方法并調用subscribe方法將所有訂閱方法注冊到EventBus中。
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 獲取事件類型,其實就是參數的class
Class<?> eventType = subscriberMethod.eventType;
// 獲取訂閱了某種事件類型數據的Subscription。
// 使用CopyOnWriteArrayList來保證線程安全
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// 根據訂閱的事件類型,獲取所有的訂閱者信息
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
// 將訂閱者添加到subscriptionsByEventType集合中
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) {
// CopyOnWriteArrayList的add方法相當于在index位置插入
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);
}
}
}
注冊的流程到此分析完畢,下面我們來總結一下,簡單來看主要有一下流程:
- 查找訂閱者中的訂閱方法
- 將事件注冊到EventBus
- 如果訂閱者方法為黏性的,那么立即分發黏性事件
2. post事件
register方法我們已經分析完畢,通過上個章節的分析,我們了解了EventBus如何查找訂閱方法、如何將方法注冊給EventBus,那么接下來我們就來分析EventBus的post過程。
我們從post方法入手,來一窺究竟。
EventBus.post
// Posts the given event to the event bus.
public void post(Object event) {
// 獲取當前線程的PostingThreadState
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狀態
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
post方法主要作用就是添加事件到事件隊列,并進行分發,其中一個很關鍵的類為PostingThreadState,我們來看下該類的定義。
PostingThreadState
final static class PostingThreadState {
// 當前線程的事件隊列
final List<Object> eventQueue = new ArrayList<Object>();
// 是否有事件正在分發
boolean isPosting;
// 是否為主線程
boolean isMainThread;
// 訂閱者信息
Subscription subscription;
// 待分發的事件
Object event;
// 是否被取消
boolean canceled;
}
PostingThreadState中包含了當前線程的事件隊列,以及訂閱者訂閱事件等信息,如此,我們便可以從事件隊列中取出事件分發給對應的訂閱者。
我們接著來分析事件的分發,在post方法調用了postSingleEvent分發來進行的事件分發,讓我們看下postSingleEvent方法究竟做了什么操作。
EventBus.postSingleEvent
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
// 獲取事件類型(eventType)
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));
}
}
}
通過分析,我們可以發現真正進行事件分發的方法是:postSingleEventForEventType。
EventBus.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;
}
postSingleEventForEventType方法中會向事件的所有訂閱者分發事件,其中調用了postToSubscription方法進行處理。
EventBus. postToSubscription
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
// 默認的threadmode,直接在post事件的線程中調用訂閱方法
invokeSubscriber(subscription, event);
break;
case MAIN:
// 在主線程中調用方法
// 如果post線程為主線程,直接回調方法
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);
}
}
通過分析我們看到postToSubscription方法會根據訂閱方法threadMode處理訂閱方法的線程問題,最終回調方法的調用是invokeSubscriber接口。
EventBus.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);
}
}
invokeSubscriber方法就是反射調用,從而完成最終的訂閱方法調用。
貌似到此,我們的分析流程已經結束了,但是我們是否注意到在postToSubscription方法中,由于post線程threadMode指定的線程可能并不一致,使用了poster進行訂閱方法線程切換處理,我們怎么能放過這幾個poster呢,讓我們打起精神來分析吧。
HandlerPoster
此類用于切換到主線程執行方法,postToSubscription方法中當threadMode為MAIN時,可能會使用到該類。
final class HandlerPoster extends Handler {
private final PendingPostQueue queue;
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
private boolean handlerActive;
// 切換線程時使用的HandlerPoster為:mainThreadPoster = new HandlerPoster(this,
// Looper.getMainLooper(), 10);
// 因此mainThreadPoste中處理消息時運行在主線程
// 因此請注意不要在訂閱方法中處理耗時操作,防止出現ANR
HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
// 這個方法是EventBus分發處理時調用的方法
void enqueue(Subscription subscription, Object event) {
// PendingPost封裝了event\subscription,代表待執行的事件分發請求
// PendingPost中還包含了一個靜態的ArrayList對象pendingPostPool,用于復用PendingPost對象
// obtainPendingPost方法就是從pendingPostPool獲取可復用對象
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
// 將待執行請求添加到隊列中
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
// 發送信息處理請求
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
// 處理事件請求的方法
@Override
public 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;
}
}
}
// 真正的調用訂閱者方法
// 處理完后會回收釋放PendingPost
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;
}
}
}
BackgroundPoster
final class BackgroundPoster implements Runnable {
// 待執行請求隊列
private final PendingPostQueue queue;
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
//將待執行請求加入隊列
public void enqueue(Subscription subscription, Object event) {
// 根據事件和訂閱者信息構造待執行請求
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
// 將請求加入隊列
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
// 執行請求
// 使用了緩存線程池來控制線程并發
eventBus.getExecutorService().execute(this);
}
}
}
@Override
public 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;
}
}
}
AsyncPoster
class AsyncPoster implements Runnable {
// 待執行請求隊列
private final PendingPostQueue queue;
private final EventBus eventBus;
AsyncPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
// 將待執行事件加入事件請求隊列
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
queue.enqueue(pendingPost);
eventBus.getExecutorService().execute(this);
}
@Override
public void run() {
// 直接執行
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
}
3. 注銷注冊
上面我們已經分析了EventBus的register和post過程,這兩個過程也是EventBus的核心,下面我們來分析下取消注冊的流程。
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
取消注冊的過程相對簡單,與register方法相對。
至此,我們已經分析了EventBut流程,相信大家對EventBus也有了進一步的了解。
如文章中有描述不正的地方,還請各位看官毫不留情的指出。
謝謝各位花費時間來閱讀此文。