EventBus的使用及原理解析
EventBus 是一款在 Android 開發中使用的 釋出/訂閱事件 匯流排框架,基於觀察者模式,將事件的接收者和傳送者分離,避免複雜且容易出錯的依賴關係和生命週期問題,簡化了元件之間的通訊,使用簡單、效率高、體積小!下邊是官方的 EventBus 原理圖:

EventBus 原理圖
一、使用EventBus
EventBus支援訂閱者方法在不同於釋出事件所線上程的執行緒中被呼叫。你可以使用執行緒模式來指定呼叫訂閱者方法的執行緒。這就需要了解EventBus支援的五種執行緒模式(ThreadMode):POSTING、MAIN、MAIN_ORDERED、BACKGROUND、ASYNC。
1.執行緒模式
- POSTING
POSTING是預設執行緒模式,在哪個執行緒傳送事件就在對應執行緒處理事件,避免了執行緒切換,效率高。因此,對於不要求是主執行緒並且耗時很短的簡單任務推薦使用該模式。線上程模型為POSTING的事件處理函式中儘量避免執行耗時操作,因為它會阻塞事件的傳遞,甚至有可能會引起ANR。 - MAIN
訂閱者方法將在主執行緒(UI執行緒)中被呼叫。因此,可以在該模式的訂閱者方法中直接更新UI介面。事件處理的時間不能太長,長了會導致ANR。 - MAIN_ORDERED
訂閱者在主執行緒中呼叫。該事件總是排隊等待以後交付給訂閱者,因此對post的呼叫將立即返回。這為事件處理提供了更嚴格且更一致的順序,例如,在具有MAIN執行緒模式的事件處理程式中釋出另一個事件,則第二個事件處理程式將在第一個事件處理程式之前完成,事件處理的時間不能太長,長了會導致ANR。 - BACKGROUND
訂閱者將在後臺執行緒中呼叫。如果釋出執行緒不是主執行緒,則將在釋出執行緒中直接呼叫事件處理程式方法。如果釋出執行緒是主執行緒,則EventBus使用單個後臺執行緒,該執行緒將按順序傳遞其所有事件。在此事件處理函式中禁止進行UI更新操作。 - ASYNC
無論事件在哪個執行緒中釋出,該事件處理函式都會在新建的子執行緒中執行;同樣,此事件處理函式中禁止進行UI更新操作。
2.基本使用
EventBus的使用分4步
(1)定義事件
public static class MessageEvent {... }
(2)準備訂閱者(指定模式)
@Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) {...};
(3)註冊/登出訂閱者(註冊和登出是成對出現的)
@Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); } @Override public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); }
(4)傳送事件
EventBus.getDefault().post(new MessageEvent());
3.使用示例
前面已經介紹了基本用法,下面看下使用示例:
首先配置gradle,如下所示:
implementation 'org.greenrobot:eventbus:3.1.1'
(1)定義訊息事件類
public class MessageEvent { private String message; public MessageEvent(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
(2)訂閱者處理事件
@Subscribe(threadMode = ThreadMode.MAIN) public void onShowEventMessage(MessageEvent messageEvent){ //將資料顯示到頁面上 tvMessage.setText(messageEvent.getMessage()); }
我們在MainActivity中建立一個訂閱者處理事件onShowEventMessage方法,新增Subscribe註解,ThreadMode設定為MAIN,事件的處理會在UI執行緒中執行,用TextView來展示接收到的訊息。
(3)註冊和取消訂閱事件
public class MainActivity extends AppCompatActivity { private Button btnJump; private Button btnReg; private TextView tvMessage; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnJump = findViewById(R.id.btn_jump); btnReg = findViewById(R.id.btn_reg); tvMessage = findViewById(R.id.tv_message); btnJump.setOnClickListener(v -> { //跳轉到SecondActivity startActivity(new Intent(MainActivity.this,SecondActivity.class)); }); btnReg.setOnClickListener(v -> { //註冊事件 EventBus.getDefault().register(this); }); } @Override protected void onDestroy() { super.onDestroy(); //登出事件 EventBus.getDefault().unregister(this); } }
我們在MainAtivity中點選按鈕註冊事件,在onDestroy中登出事件。當我們註冊事件後,跳轉到SecondActivity中去釋出事件。
(4)釋出者釋出事件
public class SecondActivity extends AppCompatActivity { private Button btnSend; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); btnSend = findViewById(R.id.btn_send); btnSend.setOnClickListener(v -> { //傳送事件 EventBus.getDefault().post(new MessageEvent("我要學習EventBus")); finish(); }); } }
在SecondActivity中點選按鈕釋出事件後,返回到MainActivity中,可以看到頁面上顯示的就是我們釋出的訊息“我要學習EventBus”。在這裡就不展示頁面了。
EventBus的粘性事件
除了普通事件,EventBus還支援傳送粘性事件,粘性事件就是在傳送事件之後再訂閱該事件也能收到該事件,這跟粘性廣播類似。
(1)訂閱者處理粘性事件
@Subscribe(threadMode = ThreadMode.POSTING, sticky = true) public void onShowStickyEventMessage(MessageEvent messageEvent){ //顯示粘性事件 tvMessage.setText(messageEvent.getMessage()); }
在MainActivity中新寫一個方法用來處理粘性事件,可以看到和普通事件基本類似,在註解中多加了一個sticky = true屬性。我們先不註冊事件,直接跳轉到MainActivity中,回來後在點選註冊事件,看看效果。
(2)傳送粘性事件
btnSend.setOnClickListener(v -> { //傳送事件 EventBus.getDefault().postSticky(new MessageEvent("我要學習EventBus的粘性事件")); finish(); });
我們在SecondActivity中點選按鈕傳送粘性事件,我們可以看到普通事件是post()方法,而粘性事件是postSticky()方法,這點需要注意。再者,我們傳送後,回到MainActivity中點選註冊事件,我們就可以看到傳送的事件了。
三、原始碼解析EventBus
下面我們看下EventBus的原始碼
1.註冊訂閱者
1.1EventBus構造方法
EventBus.getDefault().register(this);
我們在使用EventBus時,首先會呼叫EventBus.getDefault()來獲取例項,其中getDefault()是一個單例方法,保證當前只有一個EventBus例項:
public static EventBus getDefault() { if (defaultInstance == null) { synchronized (EventBus.class) { if (defaultInstance == null) { defaultInstance = new EventBus(); } } } return defaultInstance; }
這個採用了雙重檢查模式的單例模式,下面看下EventBus的構造方法做了什麼事情:
public EventBus() { this(DEFAULT_BUILDER); }
this呼叫了EventBus的另一個構造方法。而DEFAULT_BUILDER是預設的EventBusBuilder ,用來構造EventBus。
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
又建立EventBusBuilder物件,構造一個EventBusBuilder來對EventBus進行配置。
EventBus(EventBusBuilder builder) { logger = builder.getLogger(); subscriptionsByEventType = new HashMap<>(); typesBySubscriber = new HashMap<>(); stickyEvents = new ConcurrentHashMap<>(); //主執行緒初始化操作 mainThreadSupport = builder.getMainThreadSupport(); mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null; //子執行緒初始化操作 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; }
這裡採用了建造者模式。當然我們也可以通過配置EventBusBuilder來更改EventBus的屬性,如下方式也可以註冊:
EventBus.builder() .eventInheritance(false) .logSubscriberExceptions(false) .build() .register(this);
下面我們看下register方法
public void register(Object subscriber) { //獲取類 通過debug可以看出獲取出來的結果是com.monkey.eventbus.MainActivity Class<?> subscriberClass = subscriber.getClass(); //找出傳進來的訂閱者的所有訂閱方法 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); //進行同步操作 synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } }
可以看到register()方法主要分為查詢和註冊兩部分。
1.2查詢訂閱者的訂閱方法
我們先看下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 類中主要儲存了訂閱方法的Method物件,執行緒模式、事件型別、優先順序、是否是粘性事件
下面來看查詢的過程,findSubscriberMethods()方法:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { //從快取中查詢是否有訂閱方法,返回一個集合 List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); //如果快取中存在訂閱方法,則直接返回 if (subscriberMethods != null) { return subscriberMethods; } //根據 ignoreGeneratedIndex屬性值來選擇採用何種方法查詢訂閱方法 //ignoreGeneratedIndex預設為false,是否忽略註解器生成的 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; } }
findSubscriberMethods()流程很清晰,即先從快取中查詢,如果找到則直接返回,否則去做下一步的查詢過程,然後快取查詢到的集合。我們在專案中經常通過EventBus單例模式來獲取預設的EventBus物件,也就是ignoreGeneratedIndex為false的情況,這種情況會呼叫findUsingInfo()方法:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { //FindState其實就是一個裡面儲存了訂閱者和訂閱方法資訊的一個實體類,包括訂閱類中所有訂閱的事件型別和所有的訂閱方法等。 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); }
findUsingInfo()方法會在當前要註冊的類以及其父類中查詢訂閱事件的方法,最後再通過getMethodsAndRelease方法對findState做回收處理並返回訂閱方法的List集合。具體的查詢過程在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); // 如果當前方法使用了Subscribe註解 if (subscribeAnnotation != null) { // 得到該引數的型別 Class<?> eventType = parameterTypes[0]; // checkAdd()方法用來判斷FindState的anyMethodByEventType map是否已經新增過以當前eventType為key的鍵值對,沒新增過則返回true if (findState.checkAdd(method, eventType)) { // 得到Subscribe註解的threadMode屬性值,即執行緒模式 ThreadMode threadMode = subscribeAnnotation.threadMode(); // 建立一個SubscriberMethod物件,並新增到subscriberMethods集合 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註解),獲取相關屬性值,新增到SubscriberMethod物件,將符合條件的物件新增到subscriberMethods集合中,即儲存到findState中。
1.3訂閱者的註冊過程
在查詢完訂閱者的訂閱方法後,就要對其進行註冊。呼叫subscribe()方法進行註冊
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { // 得到當前訂閱了事件的方法的引數型別 Class<?> eventType = subscriberMethod.eventType; // Subscription類儲存了要註冊的類物件以及當前的subscriberMethod Subscription newSubscription = new Subscription(subscriber, subscriberMethod); // subscriptionsByEventType是一個HashMap,儲存了以eventType為key,Subscription物件集合為value的鍵值對 // 先查詢subscriptionsByEventType是否存在以當前eventType為key的值 CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions == null) { // 如果不存在,則建立一個subscriptions,並儲存到subscriptionsByEventType 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) { // 新增上邊建立的newSubscription物件到subscriptions中 subscriptions.add(i, newSubscription); break; } } // typesBySubscriber也是一個HashMap,儲存了以當前要註冊類的物件為key,註冊類中訂閱事件的方法的引數型別的集合為value的鍵值對 // 查詢是否存在對應的引數型別集合 List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null) { // 不存在則建立一個subscribedEvents,並儲存到typesBySubscriber 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>). // stickyEvents就是傳送粘性事件時,儲存了事件型別和對應事件 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); } } }
subscribe()方法需要兩個引數,第一個是訂閱者,第二個是訂閱者方法,然後將符合條件的訂閱者資訊儲存到subscriptionsByEventType、typesBySubscriber兩個HashMap中,當我們在傳送事件的時候要用到subscriptionsByEventType,完成事件的處理,當取消 EventBus 註冊的時候要用到typesBySubscriber、subscriptionsByEventType,完成相關資源的釋放。
處理粘性事件就是在 EventBus 註冊時,遍歷stickyEvents,如果當前要註冊的事件訂閱方法是粘性的,並且該方法接收的事件型別和stickyEvents中某個事件型別相同或者是其父類,則取出stickyEvents中對應事件型別的具體事件,通過checkPostStickyEventToSubscription()方法做進一步處理。
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) { if (stickyEvent != null) { // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state) // --> Strange corner case, which we don't take care of here. postToSubscription(newSubscription, stickyEvent, isMainThread()); } }
最終還是通過postToSubscription()方法完成粘性事件的處理,這就是粘性事件的整個處理流程。
註冊訂閱者總結:
(1).首先用register()方法註冊一個訂閱者
(2).獲取該訂閱者的所有訂閱的方法
(3).根據該訂閱者的所有訂閱的事件型別,將訂閱者存入到每個以 事件型別為key 以所有訂閱者為values的map集合中
(4).然後將訂閱事件新增到以訂閱者為key 以訂閱者所有訂閱事件為values的map集合中
(5).如果是訂閱了粘性事件的訂閱者,從粘性事件快取區獲取之前傳送過的粘性事件,響應這些粘性事件。
2.登出訂閱者
下面看下注銷訂閱者
EventBus.getDefault().unregister(this);
登出訂閱者是呼叫unregister()方法
public synchronized void unregister(Object subscriber) { // 得到當前註冊類物件 對應的訂閱事件方法的引數型別的集合 List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); if (subscribedTypes != null) { // 遍歷引數型別集合,釋放之前快取的當前類中的Subscription for (Class<?> eventType : subscribedTypes) { unsubscribeByEventType(subscriber, eventType); } // 刪除以subscriber為key的鍵值對 typesBySubscriber.remove(subscriber); } else { logger.log(Level.WARNING, "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); if (subscription.subscriber == subscriber) { // 如果當前subscription物件對應的註冊類物件和要取消註冊的註冊類物件相同,則刪除當前subscription物件 subscription.active = false; subscriptions.remove(i); i--; size--; } } } }
我們可以看到在unregister()方法中,釋放了typesBySubscriber、subscriptionsByEventType中快取的資源。
登出訂閱者總結:
(1).首先通過unregister方法拿到要取消的訂閱者
(2).得到該訂閱者的所有訂閱事件型別
(3).遍歷事件型別,根據每個事件型別獲取到所有的訂閱者集合,並從集合中刪除該訂閱者
3.傳送及處理事件
當我們要傳送普通事件,就需要用到post()方法,
當我們要傳送粘性事件,就需要用到postSticky()方法
//傳送普通事件 EventBus.getDefault().post("我要學習EventBus"); //傳送粘性事件 EventBus.getDefault().postSticky(new MessageEvent("我要學習EventBus的粘性事件"));
3.1粘性事件 postSticky()方法
public void postSticky(Object event) { synchronized (stickyEvents) { stickyEvents.put(event.getClass(), event); } // Should be posted after it is putted, in case the subscriber wants to remove immediately post(event); }
postSticky()方法主要做了兩件事,先將事件型別和對應事件儲存到stickyEvents中,方便後續使用;然後執行post(event)繼續傳送事件,這個post()方法就是之前傳送普通訊息的post()方法。所以,如果在傳送粘性事件前,已經有了對應型別事件的訂閱者,不是非粘性的,依然可以接收到傳送出的粘性事件。
3.2普通事件 post()方法
public void post(Object event) { //currentPostingThreadState是一個PostingThreadState型別的ThreadLocal // PostingThreadState類儲存了事件佇列和執行緒模式等資訊 PostingThreadState postingState = currentPostingThreadState.get(); List<Object> eventQueue = postingState.eventQueue; // 將要傳送的事件新增到事件佇列 eventQueue.add(event); // isPosting預設為false 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; } } }
post()方法先將傳送的事件儲存的事件佇列,然後通過迴圈出佇列,將事件交給postSingleEvent()方法處理:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { Class<?> eventClass = event.getClass(); boolean subscriptionFound = false; // eventInheritance預設為true,表示是否向上查詢事件的父類 if (eventInheritance) { // 查詢當前事件型別的Class,連同當前事件型別的Class儲存到集合 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)); } } }
postSingleEvent()方法中,根據eventInheritance屬性,決定是否向上遍歷事件的父型別,然後用postSingleEventForEventType()方法進一步處理事件:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { CopyOnWriteArrayList<Subscription> subscriptions; synchronized (this) { // 獲取事件型別對應的Subscription集合 subscriptions = subscriptionsByEventType.get(eventClass); } if (subscriptions != null && !subscriptions.isEmpty()) { for (Subscription subscription : subscriptions) { // 記錄事件 postingState.event = event; // 記錄對應的subscription postingState.subscription = subscription; boolean aborted = false; try { // 最終的事件處理 postToSubscription(subscription, event, postingState.isMainThread); aborted = postingState.canceled; } finally { //將postingState置為初始狀態 postingState.event = null; postingState.subscription = null; postingState.canceled = false; } if (aborted) { break; } } return true; } return false; }
遍歷傳送的事件型別對應的Subscription集合,然後呼叫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 { // 如果是在子執行緒傳送事件,則將事件入佇列,通過Handler切換到主執行緒執行處理事件 mainThreadPoster.enqueue(subscription, event); } break; case MAIN_ORDERED: if (mainThreadPoster != null) { // 無論在那個執行緒傳送事件,都先將事件入佇列,然後通過 Handler 切換到主執行緒,依次處理事件。 mainThreadPoster.enqueue(subscription, event); } else { // temporary: technically not correct as poster not decoupled from subscriber invokeSubscriber(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()方法就是根據訂閱事件方法的執行緒模式、以及傳送事件的執行緒來判斷如何處理事件。
傳送事件總結:
(1).首先獲取當前執行緒的事件佇列
(2).將要傳送的事件新增到事件佇列中
(3).根據傳送事件型別獲取所有的訂閱者
(4).根據響應方法的執行模式,在相應執行緒通過反射執行訂閱者的訂閱方法
4.Subscribe註解
EventBus3.0 開始用Subscribe註解配置事件訂閱方法,不再使用方法名了,如:
@Subscribe(threadMode = ThreadMode.MAIN) public void onShowEventMessage(MessageEvent messageEvent) { tvMessage.setText(messageEvent.getMessage()); }
看下Subscribe註解的實現:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Subscribe { // 指定事件訂閱方法的執行緒模式,即在那個執行緒執行事件訂閱方法處理事件,預設為POSTING ThreadMode threadMode() default ThreadMode.POSTING; // 是否支援粘性事件,預設為false boolean sticky() default false; // 指定事件訂閱方法的優先順序,預設為0,如果多個事件訂閱方法可以接收相同事件的,則優先順序高的先接收到事件 int priority() default 0; }
所以在使用Subscribe註解時可以根據需求指定threadMode、sticky、priority三個屬性。關於threadMode的屬性,文章開始已經介紹了。
關於EventBus的原始碼就學習到這裡,如理解有誤,還望指正!