EventBus 是一款在 Android 開發中使用的發佈/訂閱事件總線框架,基於觀察者模式,將事件的接收者和發送者分離,避免複雜且容易出錯的依賴關係和生命週期問題,簡化了組件之間的通信,使用簡單、效率高、體積小!下邊是官方的 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的源碼就學習到這裏,如理解有誤,還望指正!