版權聲明:本文章原創於 RamboPan ,未經允許,請勿轉載。
源碼基於 EventBus 3.0.0
EventBus
根據官方 github 的介紹,我們使用它需要三步(可以把註冊拆出來,變成四步)。
- 創建一個事件類,作爲消息,類似 Handler 中使用的 Message 類。(如果需要攜帶內容,增加成員變量)
- 增加一個處理該消息的方法,傳入參數類型爲上一步定義的事件類。並且增加 @Subscribe 註解。
- 在發送消息之前註冊,在不使用接收之後反註冊。(這裏使用的是 Actitvity 中的 onStart() 與 onStop())
- 在需要發送事件消息時,使用 Event.getDefault().post(new MessageEvent()) 就可以將消息發出。
從使用步驟上來說看着很簡單,那我們跟着這四步的使用方法來看一看源碼中的處理過程。
第一步,定義一個新的類,沒什麼說的。
第二步,看看 @Subscribe 註解。
@Subscribe
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
/**
* If true, delivers the most recent sticky event (posted with
* {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
*/
boolean sticky() default false;
/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */
int priority() default 0;
}
因爲該註解需要在運行時使用,查找該類中帶有 @Subscribe 方法,所以 Retention 爲 RetentionPolicy.RUNTIME。僅針對方法進行註解,Target 爲 ElementType.METHOD。
@Subscribe 中有三個變量,先說後兩個:
-
sticky() 爲粘性,從註釋上來看,好像不太明白,那先暫時不管,後面從源碼部分去尋找答案,默認設置爲 false。
-
priority() 爲優先級,這個參數在平常接觸線程中看到過,因爲我們一般開發中沒有對優先級有特殊要求,所以暫時也不過多關心,默認設置爲 0。
-
ThreadMode() 爲線程模式,是一個枚舉類,點開可以看到有四種模式:POSTING 、MAIN 、BACKGROUND 、 ASYNC。從名字上可以看出應該是和主線程或者非主線程以及其他情況下進行處理的。源碼中解釋部分太長,我就簡單的翻譯下:
-
- POSTING 爲直接進行處理,發出消息與接收消息線程相同,這是默認情況。
-
- MAIN 爲主線程模式。
-
- BACKGROUND 爲後臺線程模式。
-
- ASYNC 爲異步模式。
POSTING 與 MAIN 模式應該比較好理解,BACKGROUND 本來也是後臺,那和 ASYNC 中有什麼區別 ?
註釋中給出的說明是,如果是一個耗時的任務,比如網絡,就使用 ASYNC模式,如果是不太耗時,就儘量使用 BACKGROUND。這個結論我們暫時先記下,一會從源碼中找區別。
接下來看第三步,註冊與反註冊,當然先看 EventBus.getDefault();
getDefault()
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
static volatile EventBus defaultInstance;
根據 getDefault() 能看出來是單例模式,再看 EventBus 的構造函數。
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
……
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
executorService = builder.executorService;
……
}
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
EventBusBuilder() {}
能看到有兩個構造函數,當然最後還是調用的帶參的構造函數,能看到裏面有兩大類別的初始化:
- 一種是 new 出來的,比如容器 Map 類的變量。
- 一種是從 EventBusBuilder 中傳遞過來的。不過 EventBusBuilder 的構造函數並沒有其他操作,那麼 EventBus 中的某些值初始化,就是從 EventBusBuilder 中的初始值中拿到的。
有個小細節: EventBus 的構造函數修飾符,不帶參數的是 public 的。
意思是我們也可以 new 一個出來。帶參數的是 package 的,我們也無法調用,但我們 new 出來的 EventBus 仍然是使用內部的 DEFAULT_BUILDER ,仍然是單例。
接下來是 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);
}
}
}
看看大概邏輯,先拿到對應的訂閱對象的 class,然後調用 subscriberMethodFinder.findSubscriberMethods(subscriberClass) 獲得一個 SubscriberMethod 列表。
再讓每個訂閱方法進行訂閱,看着好像不太多,那我們一步一步來。先找找 subscriberMethodFinder 這個是從哪來的。
EventBus(EventBusBuilder builder) {
……
subscriberMethodFinder = new SubscriberMethodFinder(
builder.subscriberInfoIndexes,
builder.strictMethodVerification,
builder.ignoreGeneratedIndex);
}
SubscriberMethodFinder(List<SubscriberInfoIndex> subscriberInfoIndexes,
boolean strictMethodVerification,
boolean ignoreGeneratedIndex) {
this.subscriberInfoIndexes = subscriberInfoIndexes;
this.strictMethodVerification = strictMethodVerification;
this.ignoreGeneratedIndex = ignoreGeneratedIndex;
}
public class EventBusBuilder {
……
boolean ignoreGeneratedIndex;
boolean strictMethodVerification;
List<SubscriberInfoIndex> subscriberInfoIndexes;
}
subscriberMethodFinder 是在 EventBus 中的構造函數中初始化的,三個參數的值,都是從 builder 中傳入的。
但是我們第一次初始化的時候,EventBusBuilder 對應三個參數變量肯定都是 false 與 null。確定了這個之後,我們看回 findSubscriberMethods()。
findSubscriberMethods()
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;
}
}
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
先從 METHOD_CACHE 中查找該類是否存入過列表,很顯然,我們第一次使用,是沒有的所以 subscriberMethods == null ,看下面一步。
ignoreGeneratedIndex 從上面的構造方法說過,這個值沒有初始化過,所以傳入的 false,那我們走 findUsingInfo()。
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);
}
先來看 prepareFindState()。
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
if (state != null) {
FIND_STATE_POOL[i] = null;
return state;
}
}
}
return new FindState();
}
private static final int POOL_SIZE = 4;
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
static class FindState {
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
Class<?> subscriberClass;
Class<?> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
……
}
FindState 類,大概瀏覽下成員變量,就是一個存儲信息的一個類。FIND_STATE_POOL 是一個保存 FindState 的數組,初始大小爲 4。
因爲這個數組,之前也沒有調用的,長度爲 4 ,但是每個值都是 null,for 循環找不到一個非 null ,默認返回一個 new FindState。接下來是 findState.initForSubscriber(subscriberClass);
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
對 subscriberClass 與 class 賦值之後,while (findState.clazz != null) 至少第一次是滿足的,我們看進去。
private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
emmm …… 我們好像都沒有,所以返回 null。
if (findState.subscriberInfo != null) {
……
} else {
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");
}
}
}
這一步是從訂閱類中尋找帶有註解的方法。先嚐試 class.getDeclaredMethods() ,如果獲取不到再嘗試 class.getMethods()。
getDeclaredMethods 不包含繼承類方法,getMethods 包含。
fat classes like Activities 是亮點哈。
下面那麼多嵌套,都是一些判斷:是否修飾符爲 public ,是否爲非靜態方法,是否方法傳入參數個數爲 1,該方法是否帶有 @Subscribe 註解 …… 然後檢查是否添加過。
boolean checkAdd(Method method, Class<?> eventType) {
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
return true;
} else {
if (existing instanceof Method) {
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
// Paranoia check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" the existing Method
anyMethodByEventType.put(eventType, this);
}
return checkAddWithMethodSignature(method, eventType);
}
}
因爲我們之前沒有插入過方法,那麼 existing == null ,return true,代表數據插入成功。
添加方法完成後,然後 new SubscriberMethod(),並把對應參數都放入構造函數,最後由 findState.subscriberMethods 添加進列表當中。此時 findState 中就有部分數據了。
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(
new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(),
subscribeAnnotation.sticky()));
}
接下來是 moveToSuperclass()。
void moveToSuperclass() {
if (skipSuperClasses) {
clazz = null;
} else {
clazz = clazz.getSuperclass();
String clazzName = clazz.getName();
/** Skip system classes, this just degrades performance. */
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
clazz = null;
}
}
}
之前在獲取方法時,有兩種情況。如果 getDeclaredMethods() 方法失敗,那麼就 getMethods() ,此時因爲獲取了所有方法,所以 skipSuperClasses 就設爲 ture,因爲已經不需要遍歷父級。
假設我們之前走 getDeclaredMethods() 方法。此時將 findState 中的 clazz 變爲父級的 Class ,如果是 java 的類或者 android 的類則跳過查找。while (findState.clazz != null) 滿足條件,繼續循環。
findState.subscriberInfo 從剛纔的信息中沒看出變化,所以仍然和剛纔的循環一樣,直到沒有父類。最後調用 getMethodsAndRelease()。
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
findState.recycle();
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
if (FIND_STATE_POOL[i] == null) {
FIND_STATE_POOL[i] = findState;
break;
}
}
}
return subscriberMethods;
}
void recycle() {
subscriberMethods.clear();
anyMethodByEventType.clear();
subscriberClassByMethodKey.clear();
methodKeyBuilder.setLength(0);
subscriberClass = null;
clazz = null;
skipSuperClasses = false;
subscriberInfo = null;
}
最後把 List < SubscriberMethod > 進行返回,findState 通過 recycle() 重置了信息,放入 FIND_STATE_POOL 中。
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;
}
此時如果沒有找到符合接受消息的方法,將會拋出異常(權限修飾符,是否靜態,是否帶註解,是否參數個數爲1)。如果有符合的方法,放入 METHOD_CACHE 中。
METHOD_CACHE 結構: Map < Class < 註冊 > , List< SubscriberMethod >>
EventBus.register() 方法就分析了一半了,說多不多,說少不少,接着看另外半部分。
獲取到了對應的信息,那麼接下來就是分別註冊了,使用 subscribe() 進行訂閱,可能會出現多線程註冊,所以帶有 synchronized 關鍵字。
subscribe()
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
//此時是將訂閱對象 與 訂閱方法組成一個新的數據結構,
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//subscriptionsByEventType 是以事件類型爲鍵的 map,存入對應的 List<Subscription>。
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);
}
}
//通過按優先級放入列表中,此時完成了 @Subscibe 中 priority 設定對應的處理。
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;
}
}
//typesBySubscriber 是以 subscriber 爲鍵,插入 List<Class<?>> 的結構, class<?> 爲 消息事件的類。
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);
}
}
}
中間 for 循環, subscriptions.add 時完成了對 @Subscribe.priority 的實現。
假設我們 subscribe 4 次,優先級分別爲 0 , 20, 10, 0 。
-
第一次進來,size = 0 ; subscriptions 直接添加進入列表中。
-
第二次進來,size = 1 ; 20 優先級 與 subscriptions.get(0)(0 優先級)進行判斷,i == 0 , size = 1 ;第一個條件不滿足,20 > 10 滿足條件,subscriptions.add(0)。此時列表中爲 20 ,0。
-
第三次進來,size = 2 ; 10 優先級 與 subscriptions.get(0)(20 優先級)進行判斷,i == 0 , size = 2 ;第一條件不滿足,10 < 20 不滿足條件,進行下一次判斷。10 優先級 與 subscriptions.get(1) (0 優先級)判斷 , i == 1 ,size = 2 ;第一條件不滿足,10 > 0 滿足條件,subscriptions.add(1) 。此時列表中爲 20 ,10 ,0。
-
第四次進來,size = 3 ;按照之前一樣的分析,應該是 i = 3 , size = 3 時滿足條件,那麼就是放最後,此時列表爲 20 , 10 , 0 , 0。
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;
}
}
到此 register() 分析完成。接下來看 unregister() 。
unregister();
public synchronized void unregister(Object subscriber) {
//這個列表是 <訂閱對象,List<事件消息.class>> 的數據結構。
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());
}
}
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
//通過事件類型,拿到對應 Subscription ,此類是 保存 訂閱對象 與 訂閱方法的。
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);
//挨着尋找是否有相同的訂閱對象的 Subscription ,如果有則從中移除。
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
unregister() 比 register() 就簡單多了,把加入列表的重新移除就行了。
remove() 之後,List 自動把元素向前移動了,所以此時 i 還是用原來的值,但是 size 需要減小。
那就只剩最後一步 post() 。
post()
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
……
}
}
private final ThreadLocal<PostingThreadState> currentPostingThreadState =
new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
/** 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;
}
currentPostingThreadState 爲 ThreadLocal 類型,保存對於同一變量在不同線程狀態下的信息。
從 PostingThreadState 的構造函數可以看出,除了 eventQueue 被初始化了,其他都是默認 false 或 null 值。
將 event 添加進列表後,接着看 if (!postingState.isPosting),因爲第一次發消息,根據默認值 false ,滿足條件。
if (!postingState.isPosting) {
//對比當前現成的 looper 是否對應 mainLooper,如果是則爲主線程,反之爲非主線程。
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()) {
//嘗試 eventQueue.isEmpty() 不爲空時,remove 隊列最前的消息,進行 postSingleEvent
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
//完成之後重新設置標誌位。其實既然都是 threadlocal 變量了,感覺 isMainThread 沒必要多次設置。
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
邏輯比較簡單,註釋中也說明了,然後看 postSingleEvent()。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//拿到對應消息時間的類
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//構造函數中默認爲 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);
}
//如果沒有找到對應的 subscription 對象,那麼進行一些發出消息的處理。
if (!subscriptionFound) {
//是否打印日誌。
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
//這個操作比較亮,自己放入一個 NoSubscriberEvent 事件,可以通過在方法中增加對此事件的監聽。
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
eventInheritance 的值可以從 EventBus 構造函數中得出,是 true 。如果是 true ,那麼就是走上面, lookupAllEventTypes。
EventBus(EventBusBuilder builder) {
……
eventInheritance = builder.eventInheritance;
}
public class EventBusBuilder {
……
boolean eventInheritance = true;
EventBusBuilder() {}
}
/** Looks up all Class objects including super classes and interfaces.
Should also work for interfaces. */
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
synchronized (eventTypesCache) {
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<>();
Class<?> clazz = eventClass;
while (clazz != null) {
eventTypes.add(clazz);
addInterfaces(eventTypes, clazz.getInterfaces());
clazz = clazz.getSuperclass();
}
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}
從 lookupAllEventTypes 代碼上來看,eventTypesCache 的結構是這樣的。
Map < Class < 消息事件 > , List < Class <消息事件及其父類 >>>。
當然我們自定義的一個消息類,clazz.getSuperclass() 爲 Object ,所以這個列表實際上只有 eventClass 以及Obejct 類本身。並且我們單純定義的消息事件類,並不實現任何接口,所以暫時不考慮 addInterfaces() 的邏輯。
那重點就是 postSingleEventForEventType() ,我們至此都沒有看到消息時怎麼處理的,ThreadMode 對應不同的值,也沒有看到相關的邏輯。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
//通過事件class 拿到對應的 subscription,就是 包含訂閱對象與訂閱方法的結構。
synchronized (this) {
//此處因爲 Object 類沒有放在 map 中,所以 get(Object.class) == null 直接返回false.
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
//如果拿到對應的 subscription 不爲 null,則分別進行處理。
for (Subscription subscription : subscriptions) {
//此處 postingState.event 是方便後面如果需要 cancel。
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
postToSubscription(subscription, event, postingState.isMainThread);
//postingState.canceled 默認爲 false,如果調用了 cancelEventDelivery ,則改爲了 true。
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
//如果調用了 cancelEventDelivery ,則 aborted = true,中斷後面的事件。
if (aborted) {
break;
}
}
return true;
}
return false;
}
仍然麼有看到 ThreadMode 有關,還剩一個方法 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);
}
}
終於看到了這個,證明方向沒錯。
Thread.Mode_POSTING
//POSTING
//方法很直接,拿到對應的方法,然後調用 invoke ,傳入需要的對象以及 該消息事件。
//所以也對應了註釋,直接在線程上調用,也是默認的 threadMode。
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);
}
}
//MAIN
//此處的 isMainThread 是 PostingState 中進行 looper 判斷時,保存的值。
//如果是非主線程則需要 mainThreadPoster 進行處理.
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
//BACKGROUND
//和剛纔的 Main 剛好相反,如果是非主線程,直接調用,主線程時,需要使用 backgroundPoster 進行處理。
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
//ASYNC
//這個是什麼情況都由 asyncPoster 進行處理。
asyncPoster.enqueue(subscription, event);
那現在我們分析的重點就是 3 個 Poster,先來看 mainThreadPoster。
Thread.Mode_MAIN
EventBus(EventBusBuilder builder) {
// 3 個poster都是在構造函數中初始化的。
//使用了 Looper.getMainLooper。
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
……
}
HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
先不看 HandlerPoster.enqueue() 方法,PendingPostQueue 看着不像是一個眼熟的類,像是 EventBus 自己定義的容器管理類,點開看看。
final class PendingPostQueue {
private PendingPost head;
private PendingPost tail;
synchronized void enqueue(PendingPost pendingPost) {
if (pendingPost == null) {
throw new NullPointerException("null cannot be enqueued");
}
if (tail != null) {
tail.next = pendingPost;
tail = pendingPost;
} else if (head == null) {
head = tail = pendingPost;
} else {
throw new IllegalStateException("Head present, but no tail");
}
notifyAll();
}
synchronized PendingPost poll() {
PendingPost pendingPost = head;
if (head != null) {
head = head.next;
if (head == null) {
tail = null;
}
}
return pendingPost;
}
synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
if (head == null) {
wait(maxMillisToWait);
}
return poll();
}
}
就是定義了一個鏈表的容器,加入了插入與取出的方法。那我們看回 HandlerPoster,先看 enqueue()。
//後弦是集成 Handler 的,所有可以發送消息和處理消息。
final class HandlerPoster extends Handler {
private boolean handlerActive;
void enqueue(Subscription subscription, Object event) {
//根據 subscription 與 event 組裝一個 PendingPost。
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");
}
}
}
}
//從 pendingPostPool 進行復用,如果不夠時再 new 一個新的。
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
if (size > 0) {
PendingPost pendingPost = pendingPostPool.remove(size - 1);
pendingPost.event = event;
pendingPost.subscription = subscription;
pendingPost.next = null;
return pendingPost;
}
}
return new PendingPost(event, subscription);
}
}
既然有 sendMessage() ,那麼就有 handleMessage ()方法。
@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;
}
}
}
//此方法就是直接調用 method.invoke ,加入了對 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 爲 false。
handlerActive = rescheduled;
}
}
//對 invokeSubscriber() 進行調用;
void invokeSubscriber(PendingPost pendingPost) {
Object event = pendingPost.event;
Subscription subscription = pendingPost.subscription;
PendingPost.releasePendingPost(pendingPost);
//如果取消了訂閱,那麼 active 爲 false。
if (subscription.active) {
invokeSubscriber(subscription, event);
}
}
//PendingPost 的回收。
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
// Don't let the pool grow indefinitely
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost);
}
}
}
稍微有點複雜的就是 maxMillisInsideHandleMessage 的理解,插入了消息事件,如果 Handler 沒有處理完之前隊列的消息,就超過了 maxMillisInsideHandleMessage ,則執行完此次任務後就會停止執行,發送新的消息再來執行。
直到所有事件執行完,handlerActive 纔會改爲 false,否則因爲 rescheduled = true ,返回時 handlerActive 也一直爲 true。
接下來看 BackgroundPoster.
Thread.Mode_BACKGROUND
//和 handlerPoster 差不多。
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);
//此處用的線程池進行的執行,不過 executorRunning 與 handlerActive 差不多的意義,標記正在執行任務中。
if (!executorRunning) {
executorRunning = true;
eventBus.getExecutorService().execute(this);
}
}
}
@Override
public void run() {
try {
try {
while (true) {
//從 queue 中取出 PendingPost ,如果沒有的話,返回並且標記任務執行完成。
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;
}
}
}
//和 HandlerPoster 一樣,都是調用 eventBus.invokeSubscriber(pendingPost)
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
因爲之前分析的 HandlerPoster 差不多的邏輯,唯一要看的就是 eventBus.getExecutorService() 是從哪來的。
class EventBus{
ExecutorService getExecutorService() {
return executorService;
}
EventBus(EventBusBuilder builder) {
……
executorService = builder.executorService;
}
}
class EventBusBuilder{
……
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
}
可以看到是 Builder 中自帶的 newCachedThreadPool 。那麼 BackGroundPoster 也分析完成,只剩 AsyncPoster 。
Thread.Mode_ASYNC
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);
}
}
和 BackgroundPoster 差不多,唯一區別就是 AsyncPoster 並沒有加入標誌位限制 execute() 的次數,只要來任務,就可以直接調用 execute() ,而 BackgroundPoster 做了一定的隊列處理,保證了不會佔用線程池過多資源。那麼大體分析就差不多了。
下篇文章我們將接着看 EventBus 其他細節。從 EventBus 三個方法入手看源碼(二)
此分析純屬個人見解,如果有不對之處或者欠妥地方,歡迎指出一起討論。