一文徹底搞懂EventBus 3.0原理
1.簡介
EventBus是一種用于Android的事件發(fā)布-訂閱總線框架,由GreenRobot開發(fā),Gihub地址是:EventBus。它簡化了應用程序內(nèi)各個組件之間進行通信的復雜度,尤其是碎片之間進行通信的問題,可以避免由于使用廣播通信而帶來的諸多不便。
2.Subscribe注解
自3.0開始,訂閱事件的方法開始使用了Subscribe注解,不再使用方法名了,如以下方式
@Subscribe
public void testEventBus(Object obj){
...
}
復制代碼
看下注解
@Documented
@Retention(RetentionPolicy.RUNTIME) // 注解不僅被保存到class文件中,jvm加載class文件之后,仍然存在
@Target({ElementType.METHOD}) // 作用在方法上
public @interface Subscribe {
// 指定事件訂閱方法所在的線程模式,也就是決定訂閱方法是在哪個線程,默認是POSTING模式
ThreadMode threadMode() default ThreadMode.POSTING;
// 是否支持粘性事件
boolean sticky() default false;
// 優(yōu)先級,如果指定了優(yōu)先級,則若干方法接收同一事件時,優(yōu)先級高的方法會先接收到。
int priority() default 0;
}
復制代碼
ThreadMode可以指定的模式有:
- ThreadMode.POSTING:默認的線程模式,在哪個線程發(fā)送事件就在對應線程處理事件,避免了線程切換,效率高。
- ThreadMode.MAIN:如在主線程(UI線程)發(fā)送事件,則直接在主線程處理事件;如果在子線程發(fā)送事件,則先將事件入隊列,然后通過 Handler 切換到主線程,依次處理事件。
- ThreadMode.MAIN_ORDERED:無論在哪個線程發(fā)送事件,都將事件加入到隊列中,然后通過Handler切換到主線程,依次處理事件。
- ThreadMode.BACKGROUND:與ThreadMode.MAIN相反,如果在子線程發(fā)送事件,則直接在子線程處理事件;如果在主線程上發(fā)送事件,則先將事件入隊列,然后通過線程池處理事件。
- ThreadMode.ASYNC:與ThreadMode.MAIN_ORDERED相反,無論在哪個線程發(fā)送事件,都將事件加入到隊列中,然后通過線程池執(zhí)行事件
3.register注冊
好了,要想使用Eventbus,則要先注冊它,看看如何使用
EventBus.getDefault().register(this);
復制代碼
很簡單吧,getDefault()其實就是一個單例模式,創(chuàng)建EventBus實例對象,并返回
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
復制代碼
沒啥可說的,繼續(xù)看register
先看圖
再看代碼
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass(); // 獲取傳入的要注冊類的字節(jié)碼文件
List<SubscriberMethod> subscriberMethods =
subscriberMethodFinder.findSubscriberMethods(subscriberClass); // ->>分析1
synchronized (this) {
// 遍歷訂閱方法封裝類的集合
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod); // ->> 分析4
}
}
}
復制代碼
從上面的圖可以看出,這個方法其實就是做了2件事
- 根據(jù)注冊類的字節(jié)碼文件,調(diào)用findSubscriberMethods方法,獲取該注冊類上的所有訂閱方法的信息集合。
- 遍歷這個信息集合,給2個map填充數(shù)據(jù): subscriptionsByEventType可以根據(jù)event(事件類型,訂閱方法上的參數(shù)類型)獲取所有訂閱方法信息集合。 typesBySubscriber可以根據(jù)這個注冊類,獲取這個注冊類上所有的event事件類型。
/**
* 分析1:findSubscriberMethods()
* 作用:獲取當前要進行注冊類中的所有訂閱方法,也就是找尋使用了Subscribe注解、有public修飾符、一個參數(shù)的方法
*/
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
// METHOD_CACHE: 是一個ConcurrentHashMap,key是要注冊類的字節(jié)碼文件,value是這個字節(jié)碼文件里的所有訂閱方法信息的集合,集合的元素是SubscriberMethod,它實際上就是訂閱方法的信息類,包含Method對象、線程模式、事件類型、優(yōu)先級、是否是粘性事等。
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); // 這步實際上就是看看這個注冊類的方法是否已經(jīng)緩存了,緩存過就直接根據(jù)類返回
if (subscriberMethods != null) {
return subscriberMethods;
}
// EventBus是支持EventBusBuilder的,如果我們自定義了EventBusBuilder,則ignoreGeneratedIndex為true,否則為false,我們沒自定義,所有看false
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
// ->>分析2
subscriberMethods = findUsingInfo(subscriberClass);
}
// 如果該類沒有找到訂閱方法,拋出異常
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass + " and its super classes have no public methods with the @Subscribe annotation");
} else {
// 將該注冊類的類型為key, 將這個類所有注冊方法的封裝類集合為value存入map集合
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
// ->> 返回register()方法中
}
}
復制代碼
這個方法主要作用就是根據(jù)傳入的注冊類返回該類上所有的訂閱方法的信息,先找緩存METHOD_CACHE,有就走緩存,沒有就調(diào)用findUsingInfo方法獲取訂閱方法信息集合,然后再根據(jù)注冊類為key, 訂閱方法的信息集合為value, 存入緩存(METHOD_CACHE)中。
/**
* 分析2:findUsingInfo()
* 作用:如果findState緩存了,訂閱方法信息,則使用findState里的緩存,否則調(diào)用findUsingReflectionInSingleClass方法,反射獲取訂閱方法信息。
*/
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
// FindState輔助我們查找訂閱方法的類,后面會講述
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
// findState.clazz就是我們的注冊類subscriberClass
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
// 該類第一次注冊時,findState.subscriberInfo為null, 我們走false
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 {
// ->> 分析3
findUsingReflectionInSingleClass(findState);
}
// 修改findState.clazz為subscriberClass的父類Class,即需要遍歷父類
findState.moveToSuperclass();
}
// 將查找到的方法保存在了FindState實例的subscriberMethods集合中。然后使用subscriberMethods構建一個新的List<SubscriberMethod>并返回,最后釋放掉findState
return getMethodsAndRelease(findState);
// ->> 返回到findSubscriberMethods() 方法中
}
復制代碼
/**
* 分析3:findUsingReflectionInSingleClass()
* 作用:通過反射獲取訂閱方法的信息
*/
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// 通過反射獲取訂閱類中的所有方法
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
...
}
// 遍歷方法
for (Method method : methods) {
// 獲取方法修飾符
int modifiers = method.getModifiers();
// 方法是public類型,但非abstract、static等
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
// 獲取方法的修飾類型
Class<?>[] parameterTypes = method.getParameterTypes();
// 只能是1個參數(shù)
if (parameterTypes.length == 1) {
// 獲取方法上的名為Subscribe的注解
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
// 如果該方法帶Subscribe注解
if (subscribeAnnotation != null) {
// 獲取該訂閱方法上的第一個參數(shù)類型,也就是訂閱的事件類型
Class<?> eventType = parameterTypes[0];
// checkAdd()方法用來判斷FindState中是否已經(jīng)添加過將該事件類型為key的鍵值對,沒添加過則返回true
if (findState.checkAdd(method, eventType)) {
// 獲取線程模式
ThreadMode threadMode = subscribeAnnotation.threadMode();
// 將該訂閱方法,事件類型,線程模式,優(yōu)先級,是否支持粘性事件等信息,封裝成SubscriberMethod對象,并添加到findState中的subscriberMethods集合里
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
// ->> 返回到findUsingInfo() 方法中
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
...
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
...
}
}
}
復制代碼
根據(jù)反射,獲取訂閱方法的信息數(shù)據(jù),然后將它分封裝成SubscriberMethod對象,并添加到findState的集合中。
/**
* 分析4:subscribe()
* 作用:主要就是構建2個map對象
*/
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 獲取該訂閱方法的事件類型
Class<?> eventType = subscriberMethod.eventType;
// 將訂閱方法的封裝類,再進行封裝,也就是注冊類的信息也存入了
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// subscriptionsByEventType是hashmap, 以事件類型為key, Subscription集合為value
// 先查找subscriptionsByEventType是否存在以當前事件類型為key的值
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
// 如果沒有的話
if (subscriptions == null) {
// 創(chuàng)建集合,根據(jù)事件類型,合并數(shù)據(jù)
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType);
}
}
// 添加上邊創(chuàng)建的newSubscription對象到subscriptions中
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
// 根據(jù)優(yōu)先級進行排序
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
// typesBySubscriber也是一個HashMap,保存了以當前要注冊類的對象為key,注冊類中訂閱事件的方法的參數(shù)類型的集合為value的鍵值對
// 和上面一樣,根據(jù)key先判斷,是否已經(jīng)存儲過了,如果已經(jīng)存儲過了,直接取出訂注冊類中訂閱事件的方法的參數(shù)類型的集合
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
// 是否支持粘性事件
if (subscriberMethod.sticky) {
// ->> 分析5
...
}
復制代碼
2個map構建完畢了,我們的注冊也就完事了
總結一下
傳入注冊類信息,根據(jù)反射獲取注冊類上的所有方法,遍歷這些方法,取出其中的訂閱方法(條件是,一個參數(shù),權限為public,使用了Subscribe標簽)將方法的信息封裝成SubscriberMethod對象,并存入集合,然后再遍歷這個集合,取出其中的SubscriberMethod對象,再根據(jù)注冊類的字節(jié)碼文件,合并成Subscription對象,再根據(jù)event類型,進行重新分類,存入map subscriptionsByEventType中(key 為event, value 為List),再創(chuàng)建map typesBySubscriber, 注冊類為key , list為value。 完事了。
4.unregister取消注冊
使用很簡單
EventBus.getDefault().unregister(this);
復制代碼
看圖
[圖片上傳中...(image-2118a1-1572414428651-3)]
看下代碼
public synchronized void unregister(Object subscriber) {
// ->> 分析6
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
// 如果集合不為null
if (subscribedTypes != null) {
// 遍歷集合,獲取訂閱事件的類型
for (Class<?> eventType : subscribedTypes) {
// ->> 分析7
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
復制代碼
分析6:還記得我們分析注冊時,創(chuàng)建的那2個map嗎? 其中一個是typesBySubscriber,key是注冊類,value是事件類型的集合(List), 這一步就是根據(jù)注冊類獲取該類所有訂閱方法的事件類型。
/**
* 分析7
*/
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
// 根據(jù)事件類型,獲取該事件類型所對應的訂閱方法信息的集合
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
// 如果集合不為null
if (subscriptions != null) {
// 遍歷,該事件類型所對應的訂閱方法
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
// 獲取Subscription對象,該對象包含了訂閱方法的所有信息和注冊類信息
Subscription subscription = subscriptions.get(i);
// 因為subscriptionsByEventType可不光包含了1個注冊類的信息,所以要加下面的判讀,如果該訂閱方法所在的注冊類是我們要解除的注冊類的話
if (subscription.subscriber == subscriber) {
subscription.active = false;
// 從集合中,將該訂閱方法的信息刪除掉
subscriptions.remove(i);
i--;
size--;
}
}
}
}
復制代碼
總結一下
解除綁定,其實比較簡單,主要就是運用注冊時所產(chǎn)生的2個map, 先根據(jù)typesBySubscriber,也就是根據(jù)要解除綁定的注冊類,找到這個類所擁有的所有訂閱事件,然后遍歷這些訂閱事件,再根據(jù)這些訂閱事件,在subscriptionsByEventType中找到,這個事件所對應的訂閱方法的集合,再遍歷集合,判斷該訂閱方法的注冊類信息,是否是要解除綁定的注冊類,如果是,移除該訂閱方法信息,完成解除綁定。
4.post發(fā)布事件
使用也很簡單
EventBus.getDefault().post(new Object());
復制代碼
看圖
[圖片上傳中...(image-c9216f-1572414428651-2)]
看代碼
public void post(Object event) {
// ->> 分析8
PostingThreadState postingState = currentPostingThreadState.get();
// 獲取postingState里面存的一個隊列
List<Object> eventQueue = postingState.eventQueue;
// 將要發(fā)送的事件,存入隊列中
eventQueue.add(event);
// 判斷該事件是否正在發(fā)送,如果在發(fā)送,則跳過下面的邏輯
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()) {
// ->> 分析9
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
// 重置狀態(tài)
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
復制代碼
分析8:postingState實際就是一個線程狀態(tài)的封裝類,包含事件隊列,線程狀態(tài),是否正在發(fā)送的標識位,Subscription等信息,currentPostingThreadState為ThreadLocal,這也就說明postingState為線程獨有的,不會讓其他線程共享當前線程的數(shù)據(jù)
post() 方法主要就是要先將發(fā)送的事件保存在postingState中的隊列里面,它是線程獨有的,然后通過循環(huán)隊列,將事件交給postSingleEvent()方法處理。
/**
* 分析9
*/
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
// 是否要查看所有的繼承關系
if (eventInheritance) {
// 通過lookupAllEventTypes()拿到該事件所有的父類事件類型
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
// 遍歷事件類型
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
// ->> 分析10
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);
}
// 如果我們沒有訂閱事件,則發(fā)送NoSubscriberEvent
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
復制代碼
postSingleEvent()方法中,根據(jù)eventInheritance屬性,決定是否向上遍歷事件的父類型,然后用postSingleEventForEventType()方法進一步處理事件。
/**
* 分析10
*/
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
// 還記得注冊時構建的map subscriptionsByEventType嗎?對,這步就是根據(jù)事件類型,獲取它所對應的List<subscription>也就是訂閱方法集合
subscriptions = subscriptionsByEventType.get(eventClass);
}
// 如果集合不為空
if (subscriptions != null && !subscriptions.isEmpty()) {
// 遍歷集合,取出Subscription(訂閱方法信息包裝類)
for (Subscription subscription : subscriptions) {
// 記錄事件
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
// 處理事件 ->> 分析11
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;
}
復制代碼
這個方法其實很簡單,就是根據(jù)事件類型,在subscriptionsByEventType中找到對應的訂閱方法信息的集合,然后遍歷集合,拿到訂閱方法信息的封裝類,調(diào)用postToSubscription去執(zhí)行。
/**
* 分析11
*/
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
// 根據(jù)訂閱方法設置的線程模式去執(zhí)行
switch (subscription.subscriberMethod.threadMode) {
// 默認線程模式,在哪個線程發(fā)送事件,就在哪個線程接收事件
case POSTING:
// ->> 分析12
invokeSubscriber(subscription, event);
break;
// 如果是主線程,則直接執(zhí)行,子線程加入隊列,然后通過 Handler 切換到主線程執(zhí)行
case MAIN:
if (isMainThread) {
// 主線程,直接反射執(zhí)行
invokeSubscriber(subscription, event);
} else {
// ->> 分析13
mainThreadPoster.enqueue(subscription, event);
}
break;
// 無論在哪個線程,都加隊列,通過handler 在主線程執(zhí)行
case MAIN_ORDERED:
// ->> 分析13
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
// 如果在子線程中,直接執(zhí)行,如果在主線程中,加入隊列,通過線程池執(zhí)行
case BACKGROUND:
if (isMainThread) {
// ->> 分析15
backgroundPoster.enqueue(subscription, event);
} else {
// 在子線程,直接反射執(zhí)行
invokeSubscriber(subscription, event);
}
break;
// 無論在哪個線程執(zhí)行,都加入隊列,用線程池執(zhí)行
case ASYNC:
// AsyncPoster和backgroundPoster類型,但是AsyncPoster沒有加同步鎖,這也就造成了,它每次執(zhí)行一個任務,都會開一個子線程,而backgroundPoster不會
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
/**
* 分析12:直接通過反射調(diào)用執(zhí)行
*/
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);
}
}
/**
* 分析13:mainThreadPoster為HandlerPoster, 具體分析下HandlerPoster
*/
public class HandlerPoster extends Handler implements Poster {
private final PendingPostQueue queue;
private boolean handlerActive;
......
public void enqueue(Subscription subscription, Object event) {
// 用subscription和event封裝一個PendingPost對象
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
// 加入到隊列中
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
// sendMessage()發(fā)送處理事件的消息,handleMessage()方法將被執(zhí)行,將子線程切換到主線程
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
// 遍歷隊列
while (true) {
// 出隊列,取出PendingPost對象
PendingPost pendingPost = queue.poll();
...
// ->> 分析14
eventBus.invokeSubscriber(pendingPost);
...
}
} finally {
handlerActive = rescheduled;
}
}
}
/**
* 分析14:進一步處理PendingPost對象
*/
void invokeSubscriber(PendingPost pendingPost) {
// 取出事件類型
Object event = pendingPost.event;
// 取出訂閱方法的信息封裝類
Subscription subscription = pendingPost.subscription;
// 釋放pendingPost引用的資源
PendingPost.releasePendingPost(pendingPost);
if (subscription.active) {
// 通過反射調(diào)用執(zhí)行該訂閱方法
invokeSubscriber(subscription, event);
}
}
/**
* 分析15
*/
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) {
// 用subscription和event封裝一個PendingPost對象
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
// 加入隊列
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
// 調(diào)用newCachedThreadPool線程池,執(zhí)行任務
eventBus.getExecutorService().execute(this);
}
}
}
@Override
public void run() {
try {
try {
// 循環(huán)隊列
while (true) {
// 等待1秒,取出PendingPost對象
PendingPost pendingPost = queue.poll(1000);
...
// ->> 分析14(在上面)
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
}
復制代碼
總結一下
post也不難,首先是將發(fā)送的事件保存在postingState中的隊列里面,它是線程獨有的,然后遍歷postingState中的事件隊列,拿出該線程下,所有的事件的集合,然后遍歷它,再根據(jù)subscriptionsByEventType,取出該事件所對應的所有訂閱方法,然后看是否能夠直接處理,如果能,直接反射調(diào)用訂閱方法,如果不能,直接通過HandlerPower、BackgroundPower、AsyncPower切換線程后,再進行反射調(diào)用處理。
其中HandlerPower內(nèi)部就直是封裝了個Handler,每次調(diào)用的時候,先將事件加入到隊列中,然后根據(jù)Handler切換到主線程,按順序取出隊列中的事件,反射執(zhí)行 BackgroundPower是封裝了catchThreadPool用于執(zhí)行任務, AsyncPower與它類似,但是里面沒有同步鎖,每次執(zhí)行都會新開辟一個子線程去執(zhí)行任務。
5.Sticky粘性事件
什么是粘性事件?一般來說,我們使用 EventBus 都是先準備好訂閱事件的方法,然后注冊事件,最后在發(fā)送事件,即要先有事件的接收者。但粘性事件卻恰恰相反,我們可以先發(fā)送事件,后續(xù)再準備訂閱事件的方法、注冊事件。
先看下如何使用,其實很簡單
// 發(fā)布事件
EventBus.getDefault().postSticky(new Object());
// 訂閱事件
@Subscribe(sticky = true)
public void testEventBus(Object obj){
...
}
復制代碼
事件都發(fā)送了,再注冊訂閱方法竟然還能接收到之前的事件,它是怎么做到的? 看圖
圖在下面
看代碼
public void postSticky(Object event) {
// 很簡單,將要發(fā)布的粘性事件的類型和對應事件,存入map stickyEvents中
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// 這個就是一個普通的發(fā)布事件,上文分析過了
post(event);
}
復制代碼
我們將發(fā)布的粘性事件的類型和對應事件存入了我們的map中,那么我們是在哪里執(zhí)行的尼? 還記得我們上面寫的分析5嗎?我把代碼補全下,在注冊方法中
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 構建那兩個很重要的map
...
// 如果該事件支持粘性事件的話
if (subscriberMethod.sticky) {
// 如果需要向上查找事件的父類
if (eventInheritance) {
// 遍歷我們上面存儲的粘性事件的集合,取出里面存儲的粘性事件
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
// 如果candidateEventType是eventType的子類
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
// ->> 分析16
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
/**
* 分析16
*/
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
// 如果該方法支持粘性事件
if (stickyEvent != null) {
// 上面分析過這個方法,根據(jù)線程模式,去處理事件
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
復制代碼
看,是不是很簡單,來我們總結下
總結一下
如果需要發(fā)送粘性事件的話,在發(fā)送的時候,會將粘性事件的事件類型和對應事件存儲到map stickyEvents中,等新的注冊類進行注冊的時候,如果有的訂閱方法支持粘性事件,則會在注冊的時候,取出stickyEvents里面的存儲粘性事件,然后遍歷處理事件。
好了,eventbus就分析完畢了,下面一張大圖,來加深印象
6.看圖
[圖片上傳失敗...(image-b8122c-1572415302007)]
7.最后想說的
其實這個eventbus在所有開源項目中,是屬于那種比較經(jīng)典的,里面設計的很巧妙,有興趣的小伙伴們可以手動寫一個eventbus。另外尼,在這個再留個小思考
eventbus支不支持跨進程?為什么?知道的小伙伴可以在下面留言