本文的整體結構圖
本文篇幅很長,建議收藏了找時間慢慢看
本文講解的是
'org.greenrobot:eventbus:3.1.1'
最新版,和其他版本有細微差別,思想都是一樣的。
EventBus 的簡單示例
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = false, priority = 2)
public void onMessageEvent(MyBusEvent event) {
Toast.makeText(this, event.message, Toast.LENGTH_SHORT).show();
}
複製程式碼
EventBus 的建立是一個單例模式,我們就從 getDefault() 開始講起吧。
一、EventBus 的建立(單例 + Builder 設計模式)
1.1、單例模式獲取 EventBus 例項
EventBus 的建立是一個雙重校驗鎖的單例模式。
public static EventBus getDefault() {
if (sDefaultBus == null) {
synchronized (EventBus.class) {
if (sDefaultBus == null) {
sDefaultBus = new EventBus();
}
}
}
return sDefaultBus;
}
複製程式碼
單例模式沒什麼好說的,我們都知道單例模式的建構函式是私有型別 private,但是 EventBus 的建構函式卻是 public 型別。
**這樣設計的目:**EventBus 在程式碼使用過程中不僅僅只有一條匯流排,還有其他的訂閱匯流排,訂閱者可以註冊到不同的 EventBus 匯流排,然後通過不同的 EventBus 匯流排傳送資料。
不同的 EventBus 傳送的資料是相互隔離的,訂閱者只能收到註冊了該 EventBus 匯流排內事件,而不會收到別的 EventBus 事件匯流排的資料。這樣的設計為後面的不同環境的執行緒切換創造了好的條件。
/**
* Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a central bus, consider {@link #getDefault()}.
建立一個新的 EventBus 例項,每個例項在 events 事件被髮送的時候都是一個單獨的領域,為了使用一個 事件匯流排,考慮用 getDefault() 構建。
*/
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
複製程式碼
1.2、Builder 模式構建 EventBus
這裡的程式碼我們重點看一下 this(DEFAULT_BUILDER) 裡面的 DEFAULT_BUILDER。DEFAULT_BUILDER 是一個 EventBusBuilder,從這裡可以看出 EventBus 是通過 建造者模式進行構建的,接下來我們看下是如何構建的吧。
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
//Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType
subscriptionsByEventType = new HashMap<>();
//Map<Object, List<Class<?>>> typesBySubscriber
typesBySubscriber = new HashMap<>();
//Map<Class<?>, Object> stickyEvents
stickyEvents = new ConcurrentHashMap<>();
/**
用於執行緒間排程
**/
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
//用於記錄event生成索引
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
//對已經註解過的Method的查詢器,會對所設定過 @Subscriber 註解的的方法查詢相應的Event
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
//當呼叫事件處理函式發生異常是否需要列印Log
logSubscriberExceptions = builder.logSubscriberExceptions;
//當沒有訂閱者訂閱這個訊息的時候是否列印Log
logNoSubscriberMessages = builder.logNoSubscriberMessages;
//當呼叫事件處理函式,如果異常,是否需要傳送Subscriber這個事件
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
//當沒有事件處理函式時,對事件處理是否需要傳送sendNoSubscriberEvent這個標誌
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
//是否需要丟擲SubscriberException
throwSubscriberException = builder.throwSubscriberException;
//與Event有繼承關係的類是否都需要傳送
eventInheritance = builder.eventInheritance;
//執行緒池 Executors.newCachedThreadPool()
executorService = builder.executorService;
}
複製程式碼
##二、EventBus 中幾個重要的成員變數
2.1、EventBus 中重要的 3 個 HashMap。
####2.1.1、subscriptionsByEventType
Map<Class<?>, CopyOnWriteArrayList> subscriptionsByEventType 是以 Event 為 Key,Subscriber 為 Value,當傳送這個 Event 時,可以在 Map 中找到相應的 Subscriber。
####2.1.2、typesBySubscriber
Map<Object, List<Class<?>>> typesBySubscriber 以 Subscriber 為 Key,以 Event 為 Value,當我們註冊和反註冊時,都會操作 typesBySubscriber 這個 Map。
####2.1.3、stickyEvents
Map<Class<?>, Object> stickyEvents 是管理 EventBus 的粘性事件,粘性事件的 Event 傳送出去之後,即使負責接收的 Subscriber 沒有註冊,當註冊之後,依然能收到該事件,和廣播接受者的粘性事件類似。
###2.2、EventBus 中重要的有 3 個 Post。
-
mainThreadPoster:主執行緒的 Poster
mainThreadSupport.createPoster(this)
返回的是HandlerPoster(eventBus, looper, 10)
,looper 是一個 MainThread 的 Looper。意味著這個 HandlerPoster 可能就是 Handler 的實現。 -
backgroundPoster:後臺執行緒的 Poster
-
asyncPoster:非同步執行緒的 Poster
2.2.1、mainThreadPoster # HandlerPoster
HandlerPoster 其實就是 Handler 的實現,內部維護了一個 PendingPostQueue 的訊息佇列,在 enqueue(Subscription subscription, Object event) 方法中不斷從 pendingPostPool 的 ArrayList 快取池中獲取 PendingPost 新增到 PendingPostQueue 佇列中,並將該 PendingPost 事件傳送到 Handler 中處理。
在 handleMessage 中,通過一個 while 死迴圈,不斷從 PendingPostQueue 中取出 PendingPost 出來執行,獲取到 post 之後由 eventBus 通過該 post 查詢相應的 Subscriber 處理事件。
while 退出的條件有兩個
- 獲取到的 PendingPost 為 null,即是 PendingPostQueue 已經沒有訊息可處理。
- 每個 PendingPost 在 Handler 中執行的時間超過了最大的執行時間。
HandlerPoster UML 類圖
HandlerPoster 執行過程
public class HandlerPoster extends Handler implements Poster {
private final PendingPostQueue queue;//存放待執行的 Post Events 的事件佇列
private final int maxMillisInsideHandleMessage;//Post 事件在 handleMessage 中執行的最大的時間值,超過這個時間值則會拋異常
private final EventBus eventBus;
private boolean handlerActive;//標識 Handler 是否被執行起來
protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
//從 pendingPostPool 的 ArrayList 快取池中獲取 PendingPost 新增到 PendingPostQueue 佇列中,並將該 PendingPost 事件傳送到 Handler 中處理。
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);//新增到佇列中
if (!handlerActive) {//標記 Handler 為活躍狀態
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 佇列中取出 post 事件執行
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {//如果為 null,表示佇列中沒有 post 事件,此時標記 Handelr 關閉,並退出 while 迴圈
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false; //標記 Handler 為非活躍狀態
return;
}
}
}
//獲取到 post 之後由 eventBus 通過該 post 查詢相應的 Subscriber 處理事件
eventBus.invokeSubscriber(pendingPost);
//計算每個事件在 handleMessage 中執行的時間
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;
}
}
}
final class PendingPost {
//通過ArrayList來實現PendingPost的新增和刪除
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
Object event;
Subscription subscription;
PendingPost next;
private PendingPost(Object event, Subscription subscription) {
this.event = event;
this.subscription = subscription;
}
//獲取 PendingPost
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);
}
//釋放 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);
}
}
}
}
複製程式碼
2.2.2、backgroundPoster # BackGroundPoster
BackgroundPoster 實現了 Runnable 和 Poster,enqueue() 和 HandlerPoster 中實現一樣,在上文中已經講過,這裡不再贅述。
我們來看下 run() 方法中的實現,不斷從 PendingPostQueue 中取出 pendingPost 到 EventBus 中分發,這裡注意外部是 while() 死迴圈,意味著 PendingPostQueue 中所有的 pendingPost 都將分發出去。而 AsyncPoster 只是取出一個。
final class BackgroundPoster implements Runnable, Poster {
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;
//線上程池中執行這個 pendingPost
eventBus.getExecutorService().execute(this);//Executors.newCachedThreadPool()
}
}
}
@Override
public void run() {
try {
try {
//不斷迴圈從 PendingPostQueue 取出 pendingPost 到 eventBus 執行
while (true) {
//在 1000 毫秒內從 PendingPostQueue 中獲取 pendingPost
PendingPost pendingPost = queue.poll(1000);
//雙重校驗鎖判斷 pendingPost 是否為 null
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();//再次嘗試獲取 pendingPost
if (pendingPost == null) {
executorRunning = false;
return;
}
}
}
//將 pendingPost 通過 EventBus 分發出去
//這裡會將PendingPostQueue中【所有】的pendingPost都會分發,這裡區別於AsyncPoster
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
}
複製程式碼
2.2.3、asyncPoster # AsyncPoster
class AsyncPoster implements Runnable, Poster {
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);//Executors.newCachedThreadPool()
}
@Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
}
複製程式碼
三、@Subscribe 的註解
當我們指定訂閱方法的時候,會在方法上加上註解,如下,下面我們看看這個註解的具體含義
@Subscribe(threadMode = ThreadMode.MAIN, sticky = false, priority = 2)
public void onMessageEvent(EventTest event) {
}
@Documented //命名為 java doc 文件
@Retention(RetentionPolicy.RUNTIME) //指定在執行時有效,即在執行時能保持這個 Subscribe
@Target({ElementType.METHOD}) //指定型別為 METHOD,表名用來描述方法
public @interface Subscribe {
//指定執行緒模式,可以指定在 Subscribe 中接收的 Event 所處的執行緒
ThreadMode threadMode() default ThreadMode.POSTING;
boolean sticky() default false;
int priority() default 0;
}
複製程式碼
3.1、ThreadMode
public enum ThreadMode {
POSTING,//EventBus 預設的執行緒模式
MAIN,//主執行緒
MAIN_ORDERED,//主執行緒
BACKGROUND,//後臺執行緒
ASYNC//非同步執行緒
}
複製程式碼
ThreadMode 是 enum(列舉)型別,threadMode 預設值是 POSTING。3.1.1 版本的 EventBus 新增了一種型別,共 5 種,以前的版本是 4 種。
- POSTING:事件釋出在什麼執行緒,就在什麼執行緒訂閱。故它不需要切換執行緒來分發事件,因此開銷最小。它要求 task 完成的要快,不能請求 MainThread,適用簡單的 task。
- MAIN:無論事件在什麼執行緒釋出,都在主執行緒訂閱。事件將排隊等待交付(非阻塞)。使用此模式的訂閱者必須快速返回,以避免阻塞主執行緒。
- MAIN_ORDERED:無論事件在什麼執行緒釋出,都在主執行緒訂閱。區別於 MAIN,MAIN_ORDERED 事件將始終排隊等待交付。這將確保 post 呼叫是非阻塞的。
- BACKGROUND:如果釋出的執行緒不是主執行緒,則在該執行緒訂閱,如果是主執行緒,則使用一個單獨的後臺執行緒訂閱。
- ASYNC:在非主執行緒和釋出執行緒中訂閱。當處理事件的 Method 是耗時的,需要使用此模式。儘量避免同時觸發大量的耗時較長的非同步操作,EventBus 使用執行緒池高效的複用已經完成非同步操作的執行緒。
3.2、sticky 粘性事件
粘性事件是事件消費者在事件釋出之後才註冊,依然能接收到該事件的特殊型別。
StickyEvent 與普通 Event的 普通就在於,EventBus 會自動維護被作為 StickyEvent 被 post 出來(即在釋出事件時使用 EventBus.getDefault().postSticky(new MyEvent()) 方法)的事件的最後一個副本在快取中。 任何時候在任何一個訂閱了該事件的訂閱者中的任何地方,都可以通 EventBus.getDefault().getStickyEvent(MyEvent.class)來取得該型別事件的最後一次快取。
sticky(粘性)預設值是 false,如果是 true,那麼可以通過 EventBus 的 postSticky 方法分發最近的粘性事件給該訂閱者(前提是該事件可獲得)。
3.3、Priority
priority 是 Method 的優先順序,優先順序高的可以先獲得分發事件的權利。這個不會影響不同的 ThreadMode 的分發事件順序。
四、register 註冊
- 通過反射獲取到訂閱者的 Class 物件。
- 通過 Class 物件找到對應的訂閱者方法集合。
- 遍歷訂閱者方法集合,將訂閱者和訂閱者方法訂閱起來。
register() 接收的引數為 Object 型別的訂閱者,通常也就是程式碼中 Activity 和 Fragment 的例項 this。EventBus 通過 getDefault() 來獲取例項,當我們每新建一個 EventBus 匯流排,它的釋出和訂閱事件都是相互隔離的,EventBus 如何做到釋出和訂閱相互隔離呢?我們看下 register 的實現。
public void register(Object subscriber) {
//1. 通過反射獲取到訂閱者的Class物件
Class<?> subscriberClass = subscriber.getClass();
//2. 通過Class物件找到對應的訂閱者方法集合
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
//3. 遍歷訂閱者方法集合,將訂閱者和訂閱者方法訂閱起來。
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
複製程式碼
在 1 中沒什麼可細講的,接下來看下第 2 步中的 SubscriberMethodFinder 這個類的 findSubscriberMethods() 方法。
subscriberMethodFinder 是 EventBus 的一個成員,可以看作是一個訂閱方法查詢器,是在EventBus 構造方法通過 EventBusBuilder 的一些引數構造出來的。
呼叫 findSubscriberMethods 方法,傳入訂閱者的 Class 物件,字面意思是找出訂閱者中所有的訂閱方法,用一個 List 集合來接收。
4.1、SubscriberMethodFinder # findSubscriberMethods() 詳解
//訂閱者的 Class 物件為 key,訂閱者中的訂閱方法 List 為 value
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//1. 首先在 METHOD_CACHE 中查詢該 Event 對應的訂閱者集合是否已經存在,如果有直接返回
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//2. 根據訂閱者類 subscriberClass 查詢相應的訂閱方法
if (ignoreGeneratedIndex) {//是否忽略生成 index
//通過反射獲取
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//通過 SubscriberIndex 方式獲取
subscriberMethods = findUsingInfo(subscriberClass);
}
//若訂閱者中沒有訂閱方法,則拋異常
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
// 快取訂閱者的訂閱方法 List
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
//----------------------------SubscriberMethod----------------------------
//封裝了EventBus中的引數,就是一個EventBus訂閱方法包裝類
public class SubscriberMethod {
final Method method;
final ThreadMode threadMode;
final Class<?> eventType;
final int priority;
final boolean sticky;
String methodString;
//.....涉及到的方法後文再講
}
複製程式碼
METHOD_CACHE 是一個 ConcurrentHashMap,以訂閱者的 Class 物件為 key,訂閱者中的訂閱方法 List 為 value,快取了註冊過的訂閱方法。
如果有快取則返回返回快取,如果沒有則繼續往下執行。這裡看到 ignoreGeneratedIndex 這個屬性,意思為是否忽略生成 index,是在構造 SubscriberMethodFinder 通過 EventBusBuilder 的同名屬性賦值的,預設為 false,當為 true 時,表示以反射的方式獲取訂閱者中的訂閱方法,當為 false 時,則以 Subscriber Index 的方式獲取。接下來分別分析這兩種方式。
####4.1.1、findUsingReflection() 方法解析
當 ignoreGeneratedIndex 為 true 時 --> findUsingReflection()
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
// 建立並初始化 FindState 物件
FindState findState = prepareFindState();
// findState 與 subscriberClass 關聯
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
// 使用反射的方式獲取單個類的訂閱方法
findUsingReflectionInSingleClass(findState);
// 使 findState.clazz 指向父類的 Class,繼續獲取
findState.moveToSuperclass();
}
// 返回訂閱者及其父類的訂閱方法 List,並釋放資源
return getMethodsAndRelease(findState);
}
複製程式碼
####4.1.2、findUsingInfo() 方法解析
當 ignoreGeneratedIndex 為 false 時 --> findUsingInfo()
跟反射方式的 findUsingReflection 的首尾有點類似,不同的是它是通過 SubscriberInfo 這個類來獲取訂閱方法的,那麼 SubscriberInfo 物件是怎麼獲取的呢,那麼同樣只看關鍵程式碼:getSubscriberInfo()
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//1.通過 prepareFindState 獲取到 FindState(儲存找到的註解過的方法的狀態)
FindState findState = prepareFindState();
//2.findState 與 subscriberClass 關聯
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//獲取訂閱者資訊
//通過 SubscriberIndex 獲取 findState.clazz 對應的 SubscriberInfo
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
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
// 使用反射的方式獲取單個類的訂閱方法
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
複製程式碼
FindState 封裝了所有的訂閱者和訂閱方法的集合。
static class FindState {
//儲存所有訂閱方法
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
//事件型別為Key,訂閱方法為Value
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
//訂閱方法為Key,訂閱者的Class物件為Value
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
Class<?> subscriberClass;
Class<?> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
//......
}
複製程式碼
通過 prepareFindState 獲取到 FindState 物件,根據 FindState 物件可以進行下一步判斷,
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
private FindState prepareFindState() {
//找到 FIND_STATE_POOL 物件池
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
//當找到了對應的FindState
FindState state = FIND_STATE_POOL[i];
if (state != null) {//FindState 非空表示已經找到
FIND_STATE_POOL[i] = null; //清空找到的這個FindState,為了下一次能接著複用這個FIND_STATE_POOL池
return state;//返回該 FindState
}
}
}
//如果依然沒找到,則建立一個新的 FindState
return new FindState();
}
複製程式碼
####4.1.3、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();
//忽略非 public 和 static 的方法
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
// 獲取訂閱方法的所有引數
Class<?>[] parameterTypes = method.getParameterTypes();
// 訂閱方法只能有一個引數,否則忽略
if (parameterTypes.length == 1) {
// 獲取有 Subscribe 的註解
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
// 獲取第一個引數
Class<?> eventType = parameterTypes[0];
// 檢查 eventType 決定是否訂閱,通常訂閱者不能有多個 eventType 相同的訂閱方法
if (findState.checkAdd(method, eventType)) {
// 獲取執行緒模式
ThreadMode threadMode = subscribeAnnotation.threadMode();
// 新增訂閱方法進 List
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");
}
}
}
複製程式碼
經過修飾符、引數個數、是否有註解、和訂閱者是否有 eventType 相同的方法幾層條件的篩選,最終將訂閱方法新增進 findState 的 subscriberMethods 這個 List 中。
EventBus 不僅僅獲取當前類的訂閱方法,還會獲取它所有父類的訂閱方法。
在 EventBus 中,一個訂閱者包括這個訂閱者的所有父類和子類,不會有多個方法相同的去接收同一個事件,但是有可能出現這樣一種情況,子類去訂閱了該事件,父類也去訂閱了該事件。當出現這種情況,EventBus 如何判斷?通過呼叫 checkAddWithMethodSignature() 方法,根據方法簽名來檢查。
####4.1.4、checkAdd() & checkAddWithMethodSignature() 方法解析
boolean checkAdd(Method method, Class<?> eventType) {
//事件型別為Key,訂閱方法為Value
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
//put()方法執行之後,返回的是之前put的值
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);
}
}
//訂閱方法為Key,訂閱者的Class物件為Value
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class<?> methodClass = method.getDeclaringClass();
//put方法返回的是put之前的物件
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
//如果methodClassOld不存在或者是methodClassOld的父類的話,則表明是它的父類,直接返回true。否則,就表明在它的子類中也找到了相應的訂閱,執行的 put 操作是一個 revert 操作,put 進去的是 methodClassOld,而不是 methodClass
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
return true;
} else {
//這裡是一個revert操作,所以如果找到了它的子類也訂閱了該方法,則不允許父類和子類都同時訂閱該事件,put 的是之前的那個 methodClassOld,就是將以前的那個 methodClassOld 存入 HashMap 去覆蓋相同的訂閱者。
//不允許出現一個訂閱者有多個相同方法訂閱同一個事件
// Revert the put, old class is further down the class hierarchy
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
複製程式碼
FindState 的流程
- 呼叫 prepareFindState(),從 FIND_STATE_POOL 中獲取一個 FindState,如果沒有找到,則建立一個新的 FindState。
- 將 subscriberClass 通過 FindState 進行初始化。
- 返回所有的訂閱者的方法和集合。
到此,兩種方式講解完畢。無論通過哪種方式獲取,獲取到訂閱方法 List 之後,接下來是真正訂閱的過程,回到register() 中看程式碼。
###4.2、subscribe 訂閱
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//迭代每個 Subscribe 方法,呼叫 subscribe() 傳入 subscriber(訂閱者) 和 subscriberMethod(訂閱方法) 完成訂閱,
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
複製程式碼
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
// 建立 Subscription 封裝訂閱者和訂閱方法資訊
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//可併發讀寫的ArrayList,key為EventType,value為Subscriptions
//根據事件型別從 subscriptionsByEventType 這個 Map 中獲取 Subscription 集合
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//如果為 null,表示還沒有訂閱過,建立並 put 進 Map
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
//若subscriptions中已經包含newSubscription,表示該newSubscription已經被訂閱過,丟擲異常
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType);
}
}
// 按照優先順序插入subscriptions
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;
}
}
//key為訂閱者,value為eventType,用來存放訂閱者中的事件型別
//private final Map<Object, List<Class<?>>> typesBySubscriber;
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
//將EventType放入subscribedEvents的集合中
subscribedEvents.add(eventType);
//判斷是否為Sticky事件
if (subscriberMethod.sticky) {
//判斷是否設定了事件繼承
if (eventInheritance) {
//獲取到所有Sticky事件的Set集合
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
//遍歷所有Sticky事件
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
//判斷當前事件型別是否為黏性事件或者其子類
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
// 執行設定了 sticky 模式的訂閱方法
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
複製程式碼
4.3、register 流程圖
事件的訂閱講解到這裡,接下來看看事件的分發 Post。
五、Post 分發
###5.1、Post 方法解析
一般的事件釋出方式
EventBus.getDefault().post(new EventTest());
複製程式碼
接下來看看 Post 方法的具體程式碼。
//currentPostingThreadState 執行緒獨有的
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
public void post(Object event) {
//獲取當前執行緒的 posting 狀態
PostingThreadState postingState = currentPostingThreadState.get();
//獲取當前事件佇列
List<Object> eventQueue = postingState.eventQueue;
//將事件新增進當前執行緒的事件佇列
eventQueue.add(event);
//判斷是否正在posting
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
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;
}
}
}
//傳送事件的執行緒封裝類
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<>();//事件佇列
boolean isPosting;//是否正在 posting
boolean isMainThread;//是否為主執行緒
Subscription subscription;
Object event;
boolean canceled;//是否已經取消
}
複製程式碼
EventBus 用 ThreadLocal 儲存每個執行緒的 PostingThreadState,一個儲存了事件釋出狀態的類,當 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) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
//傳送沒有訂閱者訂閱該事件
post(new NoSubscriberEvent(this, event));
}
}
}
複製程式碼
程式碼也非常簡單,首先看 eventInheritance 這個屬性,是否開啟事件繼承,若是,找出釋出事件的所有父類,也就是 lookupAllEventTypes(),然後遍歷每個事件型別進行釋出。若不是,則直接釋出該事件。
如果需要釋出的事件沒有找到任何匹配的訂閱資訊,則釋出一個 NoSubscriberEvent 事件。這裡只看釋出事件的關鍵程式碼 postSingleEventForEventType()。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
//獲取到 Subscription 的集合
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 傳送
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;
}
複製程式碼
來到這裡,開始根據事件型別匹配出訂閱資訊,如果該事件有訂閱資訊,則執行 postToSubscription(),釋出事件到每個訂閱者,返回 true,若沒有,則返回 false。繼續追蹤釋出事件到具體訂閱者的程式碼 postToSubscription()。
###5.2、postToSubscription() 方法解析
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
//訂閱執行緒跟隨釋出執行緒,EventBus 預設的訂閱方式
case POSTING:
// 訂閱執行緒和釋出執行緒相同,直接訂閱
invokeSubscriber(subscription, event);
break;
// 訂閱執行緒為主執行緒
case MAIN:
//如果在 UI 執行緒,直接呼叫 invokeSubscriber
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
//如果不在 UI 執行緒,用 mainThreadPoster 進行排程,即上文講述的 HandlerPoster 的 Handler 非同步處理,將訂閱執行緒切換到主執行緒訂閱
mainThreadPoster.enqueue(subscription, event);
}
break;
// 訂閱執行緒為主執行緒
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
break;
// 訂閱執行緒為後臺執行緒
case BACKGROUND:
//如果在 UI 執行緒,則將 subscription 新增到後臺執行緒的執行緒池
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
//不在UI執行緒,直接分發
invokeSubscriber(subscription, event);
}
break;
// 訂閱執行緒為非同步執行緒
case ASYNC:
// 使用執行緒池執行緒訂閱
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.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);
}
}
複製程式碼
訂閱者接收到了事件,呼叫訂閱方法,傳入釋出的事件作為引數,至此,事件釋出過程就結束了。
5.3、post 流程圖
六、unregister 反註冊
先看反註冊的程式碼
EventBus.getDefault().unregister(this);
複製程式碼
跟蹤 unregister()
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 {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
複製程式碼
註冊過程我們就知道 typesBySubscriber 是儲存訂閱者的所有訂閱事件型別的一個 Map,這裡根據訂閱者拿到訂閱事件型別 List,然後逐個取消訂閱,最後 typesBySubscriber 移除該訂閱者,。這裡只需要關注它是如果取消訂閱的,跟蹤 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--;
}
}
}
}
複製程式碼
subscriptionsByEventType 是儲存事件型別對應訂閱資訊的 Map,程式碼邏輯非常清晰,找出某事件型別的訂閱資訊 List,遍歷訂閱資訊,將要取消訂閱的訂閱者和訂閱資訊封裝的訂閱者比對,如果是同一個,則說明該訂閱資訊是將要失效的,於是將該訂閱資訊移除。
七、總結
回顧一下 EventBus 的三個步驟
- 註冊訂閱者
- 事件釋出
- 反註冊訂閱者
好了,EventBus 的原始碼解析到這就結束了,想進一步瞭解 EventBus 的朋友可以親自去閱讀原始碼。