今天學(xué)習(xí)了一下EventBus 的源碼,將其在接收事件時(shí)指定線程執(zhí)行的大致原理梳理了一下,提煉整理,作為學(xué)習(xí)總結(jié),也方便日后查閱。
正文
EventBus 可以在不同的線程發(fā)送事件、并在指定類型的線程接收和處理事件。在不同線程情況下,發(fā)送、接收并處理事件大致有4種情況:
A:在主線程發(fā)送,在子線程發(fā)送;B:在主線程執(zhí)行訂閱方法,在子線程執(zhí)行訂閱方法;AB兩兩搭配會(huì)有四種組合,也就是在不同線程情況下,發(fā)送、接收并處理事件的4種情況。
主要的問題其實(shí)只有兩個(gè),其一:如何判斷當(dāng)前發(fā)送事件的線程是否是主線程;其二:如何在接收事件時(shí)指定線程并執(zhí)行;
一個(gè)一個(gè)來看。
1.如何判斷是否在主線程發(fā)送
EventBus在初始化的時(shí)候會(huì)初始化一個(gè)MainThreadSupport對(duì)象,它會(huì)去獲取主線程的Looper對(duì)象并存起來。(當(dāng)前最新版本如果不是Android環(huán)境MainThreadSupport會(huì)為空,非Android環(huán)境也就無需關(guān)注是否是UI主線程的問題了)
在發(fā)送消息的時(shí)候,EventBus會(huì)取出當(dāng)前線程的Looper對(duì)象對(duì)象與主線程Looper對(duì)象做比較,如果相同,說明是在主線程發(fā)送消息,如果不同,說明是在子線程發(fā)送消息。以下是MainThreadSupport的代碼:
public interface MainThreadSupport {
boolean isMainThread();
Poster createPoster(EventBus eventBus);
class AndroidHandlerMainThreadSupport implements MainThreadSupport {
private final Looper looper;
public AndroidHandlerMainThreadSupport(Looper looper) {
this.looper = looper;
}
@Override
public boolean isMainThread() {
return looper == Looper.myLooper();
}
@Override
public Poster createPoster(EventBus eventBus) {
return new HandlerPoster(eventBus, looper, 10);
}
}
}
2.怎么在指定的線程執(zhí)行訂閱者的方法
在找到訂閱者之后,判斷不同線程情況下執(zhí)行訂閱方法的邏輯基本都在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 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:
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);
}
}
以上代碼中,如何判斷是否是主線程上面已經(jīng)說過了。
invokeSubscriber()這個(gè)方法其實(shí)就是拿到訂閱者的信息,直接執(zhí)行訂閱方法了(通過反射獲取)。
subscription對(duì)象中有一個(gè)SubscriberMethod對(duì)象,而SubscriberMethod這個(gè)對(duì)象基本上包含了訂閱者的執(zhí)行線程、訂閱方法、是否粘性事件、優(yōu)先級(jí)等等信息。如下:
public class SubscriberMethod {
final Method method;
final ThreadMode threadMode;
final Class<?> eventType;
final int priority;
final boolean sticky;
/** Used for efficient comparison */
String methodString;
//省略若干代碼...
}
所以,從postToSubscription()方法可以看出,當(dāng)threadMode是POSTING時(shí),直接在當(dāng)前線程執(zhí)行,不做判斷,也就是從哪個(gè)線程發(fā)送,就從哪個(gè)線程執(zhí)行訂閱方法;
我們這里主要來看threadMode為MAIN和BACKGROUND的情況:
在主線程執(zhí)行
當(dāng)threadMode為MAIN時(shí),如果在主線程發(fā)送,直接在當(dāng)前線程執(zhí)行,沒有問題。如果不在主線程發(fā)送,會(huì)有一個(gè)mainThreadPoster將包含訂閱者信息的對(duì)象加入隊(duì)列。這個(gè)mainThreadPoster其實(shí)是Handler的子類,它利用Handler的消息機(jī)制,發(fā)送消息并在主線程接收消息,獲取到訂閱者的信息后在主線程處理事件,從而實(shí)現(xiàn)在子線程發(fā)送消息,在主線程處理事件。
這里直接利用Hadler的現(xiàn)成機(jī)制,可謂簡(jiǎn)明高效。
在子線程執(zhí)行
當(dāng)threadMode為BACKGROUND時(shí),如果不在主線程發(fā)送,直接執(zhí)行,沒有問題。如果在主線程發(fā)送,這里有一個(gè)backgroundPoster將包含訂閱者信息的對(duì)象加入隊(duì)列。BackgroundPoster其實(shí)是Runnable的子類,在自己的run方法中不斷從隊(duì)列中取出訂閱者對(duì)象,執(zhí)行訂閱方法。EventBus維護(hù)了一個(gè)線程池,BackgroundPoster會(huì)將自己丟到線程池中,執(zhí)行自己的run方法,從而實(shí)現(xiàn)在在主線程發(fā)送事件,在子線程中執(zhí)行訂閱方法。
以上,就是EventBus切換執(zhí)行線程的主要流程。
其實(shí)并不難。
末尾附上一篇講解EventBus原理的簡(jiǎn)練博文:戳這里