項(xiàng)目地址:EventBus,本文分析版本: 3.1.1
一、概述
EventBus 是一個(gè) Android 事件發(fā)布/訂閱框架,通過(guò)解耦發(fā)布者和訂閱者簡(jiǎn)化 Android 事件傳遞,這里的事件可以理解為消息,本文中統(tǒng)一稱為事件。事件傳遞既可用于 Android 四大組件間通訊,也可以用戶異步線程和主線程間通訊等等。
傳統(tǒng)的事件傳遞方式包括:Handler、BroadCastReceiver、Interface 回調(diào),相比之下 EventBus 的優(yōu)點(diǎn)是代碼簡(jiǎn)潔,使用簡(jiǎn)單,并將事件發(fā)布和訂閱充分解耦。
-
事件(Event):又可稱為消息,本文中統(tǒng)一用事件表示。其實(shí)就是一個(gè)對(duì)象,可以是網(wǎng)絡(luò)請(qǐng)求返回的字符串,也可以是某個(gè)開(kāi)關(guān)狀態(tài)等等。
事件類型(EventType)指事件所屬的 Class。- 事件分為一般事件和 Sticky 事件,相對(duì)于一般事件,Sticky 事件不同之處在于,當(dāng)事件發(fā)布后,再有訂閱者開(kāi)始訂閱該類型事件,依然能收到該類型事件最近一個(gè) Sticky 事件(所謂「最近一個(gè)」指的就是該類型事件「最后一次發(fā)出」)。
-
訂閱者(Subscriber):訂閱某種事件類型的對(duì)象。當(dāng)有發(fā)布者發(fā)布這類事件后,EventBus 會(huì)執(zhí)行訂閱者的 onEvent 函數(shù),這個(gè)函數(shù)叫
事件響應(yīng)函數(shù)。訂閱者通過(guò) register 接口訂閱某個(gè)事件類型,unregister 接口退訂。訂閱者存在優(yōu)先級(jí),優(yōu)先級(jí)高的訂閱者可以取消事件繼續(xù)向優(yōu)先級(jí)低的訂閱者分發(fā),默認(rèn)所有訂閱者優(yōu)先級(jí)都為 0。 - 發(fā)布者(Publisher):發(fā)布某事件的對(duì)象,通過(guò) post 接口發(fā)布事件。
二、如何使用
2.1 添加依賴
方式一,運(yùn)行期處理注解
在app 的 build.gradle 文件中添加依賴
dependencies {
implementation 'org.greenrobot:eventbus:3.1.1'
}
方式二,編譯期預(yù)處理注解
Android Studio 3.0 及以上
在 app 的 build.gradle 文件中添加
android {
//……
defaultConfig {
//……
javaCompileOptions {
annotationProcessorOptions {
arguments = [eventBusIndex: 'org.greenrobot.eventbusperf.MyEventBusIndex']
}
}
}
}
dependencies {
implementation 'org.greenrobot:eventbus:3.1.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
build 之后,會(huì)生成一個(gè) MyEventBusIndex.java類。
然后在使用 EventBus 實(shí)例之前,又有兩種方式可以將配置MyEventBusIndex.java配置到類中是喲經(jīng)。
方式一 在構(gòu)造 EventBus 時(shí)傳入我們自定義的 EventBusIndex,
EventBus mEventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();
方式二 將索引應(yīng)用到默認(rèn)的單例中
使用 EventBus 之前,先調(diào)用下面的代碼初始化 EventBus。
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
2.2 定義事件類
public class CustomEvent {
private String mEventName;
public CustomEvent() {
}
public String getEventName() {
return mEventName;
}
public void setEventName(String eventName) {
mEventName = eventName;
}
}
2.3 注冊(cè)為監(jiān)聽(tīng)者
在合適的地方(比如 Activity#onCreate、Fragment#onCreateView)通過(guò)下方代碼進(jìn)行注冊(cè)
EventBus.getDefault().register(this);
2.4 編寫響應(yīng)事件的訂閱方法
@Subscribe(threadMode = ThreadMode.BACKGROUND, sticky = true, priority = 100)
public void onMessage(CustomEvent event) {
Log.d(TAG, "onMessage: " + event.getEventName());
}
使用編譯期注解處理的情況下,訂閱方法的訪問(wèn)控制權(quán)限必須是 非 private 并且非 static 的
使用運(yùn)行期反射處理的情況下,訂閱方法的訪問(wèn)控制權(quán)限必須是 public 的
- 通過(guò) ThreadMode 可以指定訂閱方法在哪個(gè)線程執(zhí)行,有四種選擇
- ThreadMode.MAIN 事件訂閱方法會(huì)在 UI 線程中執(zhí)行
- 使用此模式的事件訂閱方法必須快速返回以避免阻塞主線程。
- ThreadMode.POSTING (默認(rèn)的模式)表示事件在哪個(gè)線程中發(fā)布出來(lái)的,事件訂閱方法就會(huì)在這個(gè)線程中運(yùn)行;
- 該模式避免了線程切換,適用于那些在很短的時(shí)間內(nèi)完成的簡(jiǎn)單任務(wù),無(wú)需主線程。使用這種模式的事件訂閱方法必須快速返回以避免阻塞發(fā)布線程(發(fā)布線程可能是主線程)。
- ThreadMode.MAIN_ORDERED
- 在 Android 上,訂閱方法將在 Android 的主線程中被調(diào)用。事件將會(huì)排隊(duì)等待交付,這確保了 post 調(diào)用是非阻塞的。
- ThreadMode.BACKGROUND 子線程執(zhí)行,如果本來(lái)就在子線程,直接在該子線程執(zhí)行
- EventBus 使用一個(gè)后臺(tái)線程,將按順序發(fā)送所有事件。使用這種模式的訂閱方法應(yīng)該盡快返回以避免阻塞后臺(tái)線程。
- 注意:「一個(gè)后臺(tái)線程」所指的并不是
Executors.newSingleThreadPool(),而是使用 EventBus 在實(shí)例化時(shí)創(chuàng)建的 cacheThreadPool 中的某一個(gè)線程。
- 注意:「一個(gè)后臺(tái)線程」所指的并不是
- EventBus 使用一個(gè)后臺(tái)線程,將按順序發(fā)送所有事件。使用這種模式的訂閱方法應(yīng)該盡快返回以避免阻塞后臺(tái)線程。
- ThreadMode.ASYNC 新建子線程執(zhí)行。適用于耗時(shí)操作
- 發(fā)布事件永遠(yuǎn)不會(huì)等待使用此模式的訂閱方法。適用于比較耗時(shí)的訂閱方法,比如用于網(wǎng)絡(luò)請(qǐng)求。使用時(shí)應(yīng)該避免同時(shí)觸發(fā)大量長(zhǎng)時(shí)間運(yùn)行的異步訂閱方法來(lái)限制并發(fā)線程的數(shù)量。 EventBus 使用線程池有效地重用已完成的異步用戶通知中的線程。
- ThreadMode.MAIN 事件訂閱方法會(huì)在 UI 線程中執(zhí)行
- 通過(guò) sticky 指定是否接收粘性事件,默認(rèn)為 false
- 通過(guò) priority 設(shè)置接收訂閱方法的優(yōu)先級(jí),相同的事件,優(yōu)先級(jí)越高的訂閱方法 越早收到事件
2.5 發(fā)送事件
通過(guò)EventBus的post()方法來(lái)發(fā)送事件, 發(fā)送之后就會(huì)執(zhí)行注冊(cè)過(guò)這個(gè)事件的對(duì)應(yīng)類的方法. 或者通過(guò)postSticky()來(lái)發(fā)送一個(gè)粘性事件。
2.6 解除注冊(cè)
在合適的地方(比如 Activity#onDestroy)使用下面的代碼進(jìn)行解除注冊(cè) EventBus.getDefault().unregister(this);
2.7 小結(jié)
要實(shí)現(xiàn)訂閱,需要進(jìn)行注冊(cè),以及解注冊(cè),訂閱方法以「目標(biāo)事件」作為方法的參數(shù), 使用 Subscribe 注解,可以指定訂閱方法執(zhí)行的線程、是否接收 sticky 事件、訂閱方法的優(yōu)先級(jí)。
至于發(fā)送方,只需要?jiǎng)?chuàng)建相應(yīng)的 事件實(shí)例,然后調(diào)用 post 或者 postSticky 將事件發(fā)送出去即可。
三、實(shí)現(xiàn)
3.1 初始化 EventBus
開(kāi)發(fā)者通常是調(diào)用 EventBus#getDefault 方法獲取 EventBus 實(shí)例。
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
getDefault 通過(guò)雙重校驗(yàn)鎖的方式來(lái)實(shí)現(xiàn)單例
構(gòu)造方法
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
通過(guò) getDefault 獲取的 EventBus 對(duì)象是通過(guò)默認(rèn)的 EventBusBuilder 構(gòu)造而成的。
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();//默認(rèn)為 CachedThreadPool,不限制線程數(shù)
boolean logSubscriberExceptions = true;
boolean logNoSubscriberMessages = true;
boolean sendSubscriberExceptionEvent = true;
boolean sendNoSubscriberEvent = true;
boolean throwSubscriberException;
boolean strictMethodVerification;//
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;//默認(rèn)的線程池
List<Class<?>> skipMethodVerificationForClasses;
List<SubscriberInfoIndex> subscriberInfoIndexes;
MainThreadSupport mainThreadSupport;
EventBus(EventBusBuilder builder) {
//……
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
//……
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
主要看以下幾個(gè)單例的實(shí)現(xiàn)。
boolean eventInheritance = true;//是否允許事件繼承
boolean ignoreGeneratedIndex;//是否忽略 生成的 index,默認(rèn)為 false,也就是會(huì)先嘗試尋找編譯期注解生成的訂閱方法信息,找不到再使用反射去獲取。
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private final Map<Class<?>, Object> stickyEvents;
private final HandlerPoster mainThreadPoster;
private final BackgroundPoster backgroundPoster;
private final AsyncPoster asyncPoster;
- subscriptionsByEventType ,key 是事件類型,value 為 訂閱了該事件的方法列表
- typesBySubscriber,key 為訂閱者,value 某個(gè)訂閱者訂閱的事件列表
- stickyEvents,key 為事件類型,value 為具體的事件實(shí)例
- mainThreadPoster 主線程分發(fā)
- backgroundPoster 后臺(tái)線程分發(fā)
- asyncPoster 異步線程分發(fā)
3.2 注冊(cè)訂閱
org.greenrobot.eventbus.EventBus#register
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();//獲取訂閱者的 class 對(duì)象
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);//查找訂閱者中所有的訂閱方法
synchronized (this) {
//迭代遍歷訂閱者中所有的訂閱方法
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
EventBus#findSubscriberMethods
找出給定 class 中所有的訂閱方法
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap();//以 class 為 key,方法列表為 value 的,Map 作為緩存
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List subscriberMethods = (List)METHOD_CACHE.get(subscriberClass);//緩存中獲取
if(subscriberMethods != null) {
return subscriberMethods;//緩存命中,直接返回
} else {
if(this.ignoreGeneratedIndex) {//忽略編譯期生成的 訂閱方法信息
subscriberMethods = this.findUsingReflection(subscriberClass);//通過(guò)反射獲取訂閱方法信息
} else {
//獲取編譯期生成的 訂閱方法信息
subscriberMethods = this.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;//返回
}
}
}
EventBus#subscribe()
//必須從同步塊中調(diào)用
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;//事件類型
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);//新建一個(gè) Subscription,存儲(chǔ)訂閱的對(duì)象以及 響應(yīng)的方法
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);//Map<Class<?>, CopyOnWriteArrayList<Subscription>> 從 map 中獲取相應(yīng)訂閱類型的 列表
if (subscriptions == null) {//如果沒(méi)有則新建一個(gè)
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);//沒(méi)有則拋出異常。
}
}
int size = subscriptions.size();
//遍歷監(jiān)聽(tīng)列表,將新的 subscription 插入到正確位置。列表按照優(yōu)先級(jí)遞減的順序排序
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//如果觸發(fā)的方法 要接收「粘性事件」,獲取相應(yīng)類型的 Event 并觸發(fā)相應(yīng)的方法
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>).
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);
}
}
}
該方法會(huì)將相應(yīng)的事件插入對(duì)應(yīng)事件的列表中。如果在方法注解中聲明了 sticky,還會(huì)馬上調(diào)用該方法。
檢測(cè) stick 事件,如果相應(yīng)的事件定義有子類的話,會(huì)遍歷事件的事件子類逐一通知該方法。
3.2.1 通過(guò)反射處理注解
SubscriberMethodFinder#findUsingReflection
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
SubscriberMethodFinder#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();//獲取的是類的所有公有方法,這就包括自身 和從基類繼承的、從接口實(shí)現(xiàn)的所有 public 方法。
//getDeclareMethods 返回的是該類中定義的「所有方法」,但是不包括從父類繼承而來(lái)的方法
} catch (Throwable th) {
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) {//參數(shù)列表長(zhǎng)度為 0
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
//將訂閱方法信息添加到 findState 中
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");
}
}
}
ignoreGeneratedIndex 是什么?
由于反射成本高,而且 EventBus 3.0 引入了 EventBusAnnotationProcessor,故默認(rèn) ignoreGeneratedIndex 為 false,需要注意的是,如果設(shè)置 ignoreGeneratedIndex 為 true,則前面使用的 MyEventBusIndex 無(wú)效,還是會(huì)走反射解析的分支。
3.2.2 使用編譯期生成的訂閱方法信息
網(wǎng)上有很多介紹 EventBus 的文章,但是幾乎沒(méi)有提到 EventBusAnnotationProcessor 的。在 3.0 版本開(kāi)始,EventBus提供了一個(gè)EventBusAnnotationProcessor注解處理器來(lái)在編譯期通過(guò)讀取@Subscribe()注解并解析,處理其中所包含的信息,然后生成java類來(lái)保存所有訂閱者關(guān)于訂閱的信息,這樣就比在運(yùn)行時(shí)使用反射來(lái)獲得這些訂閱者的信息速度要快.我們可以參考EventBus項(xiàng)目里的EventBusPerformance這個(gè)例子,編譯后我們可以在build文件夾里找到這個(gè)類,MyEventBusIndex 類,當(dāng)然類名是可以自定義的.我們大致看一下生成的MyEventBusIndex類是什么樣的:
訂閱者中的 訂閱方法
public class ReceiveEventFragment extends Fragment {
//……
@Subscribe(threadMode = ThreadMode.MAIN, priority = 0)
public void onReceive(MsgEvent event) {
Log.d(TAG, "onReceive: " + event);
mTvMsg.setText(String.format("msgId:%d\nmsg:%s", event.msgId, event.msg));
}
@Subscribe(threadMode = ThreadMode.MAIN, priority = 10000)
public void ShowToast(MsgEvent event) {
Toast.makeText(getActivity(), String.format("msgId:%d\nmsg:%s", event.msgId, event.msg), Toast.LENGTH_SHORT).show();
}
}
/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();//以訂閱者為 key,以訂閱者中的 訂閱方法列表為 value 的 map
//將 ReceiveEventFragment 的訂閱信息存儲(chǔ)到 map 中
putIndex(new SimpleSubscriberInfo(com.test.commentdemo.eventbusdemo.ReceiveEventFragment.class,
true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onReceive", com.test.commentdemo.eventbusdemo.MsgEvent.class,
ThreadMode.MAIN),
new SubscriberMethodInfo("ShowToast", com.test.commentdemo.eventbusdemo.MsgEvent.class,
ThreadMode.MAIN, 10000, false),
}));//訂閱者中所有的訂閱方法
//……代碼省略(存儲(chǔ)其他訂閱者的訂閱信息到 map 中)
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);//根據(jù)類型從 Map 獲取訂閱者的訂閱信息
if (info != null) {
return info;
} else {
return null;
}
}
}
繼續(xù)前面 ignoreGeneratedIndex 為 false 時(shí),會(huì)執(zhí)行以下分支。
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
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);//如果找不到相應(yīng)的訂閱方法信息(可能使用 EventBus 實(shí)例之前,沒(méi)有將MyEventBusIndex ),需要通過(guò)反射獲取訂閱方法信息
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
findUsingInfo()方法,其無(wú)非就是通過(guò)查找我們前面所說(shuō)的MyEventBusIndex類中的信息,來(lái)轉(zhuǎn)換成List<SubscriberMethod>從而獲得訂閱類的相關(guān)訂閱函數(shù)的各種信息
SubscriberMethodFinder#getSubscriberInfo
private SubscriberInfo getSubscriberInfo(SubscriberMethodFinder.FindState findState) {
if(findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null)
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();//獲取父類的訂閱方法信息
if(findState.clazz == superclassInfo.getSubscriberClass()) {//
return superclassInfo;
}
}
if(this.subscriberInfoIndexes != null) {
Iterator superclassInfo1 = this.subscriberInfoIndexes.iterator();
//遍歷,從 index 中獲取 訂閱信息
while(superclassInfo1.hasNext()) {
SubscriberInfoIndex index = (SubscriberInfoIndex)superclassInfo1.next();
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);//從自動(dòng)生成的 MyEventBusIndex 類中的 SUBSCRIBER_INDEX 里面獲取訂閱方法信息
if(info != null) {
return info;
}
}
}
return null;
}
3.3 解注冊(cè)
/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);//獲取訂閱者訂閱的事件列表
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {//遍歷訂閱者所訂閱的事件
//從事件對(duì)應(yīng)的訂閱列表中 訂閱者的訂閱信息
unsubscribeByEventType(subscriber, eventType);
typesBySubscriber.remove(subscriber);//移除訂閱者
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//從事件對(duì)應(yīng)的訂閱列表中 訂閱者的訂閱信息
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--;
}
}
}
}
3.4 發(fā)送事件
EventBus#post
1. 獲取訂閱列表
public class EventBus {
private final ThreadLocal<EventBus.PostingThreadState> currentPostingThreadState;//線程私有變量——當(dāng)前發(fā)布線程狀態(tài)
public void post(Object event) {
EventBus.PostingThreadState postingState = (EventBus.PostingThreadState)this.currentPostingThreadState.get();//記錄發(fā)布線程的狀態(tài)(比如是否是主線程)
List eventQueue = postingState.eventQueue;//從發(fā)布狀態(tài)中獲取事件隊(duì)列
eventQueue.add(event);//添加進(jìn)事件隊(duì)列的隊(duì)尾
if(!postingState.isPosting) {//當(dāng)前不是處于分發(fā)狀態(tài)
postingState.isMainThread = this.isMainThread();//是否在主線程
postingState.isPosting = true;//將狀態(tài)改為發(fā)布中
if(postingState.canceled) {//取消發(fā)布
throw new EventBusException("Internal error. Abort state was not reset
}
try {
while(!eventQueue.isEmpty()) {//只要事件隊(duì)列非空,就一直往外取出事件并發(fā)布
this.postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
static final class PostingThreadState {
final List<Object> eventQueue = new ArrayList();//事件隊(duì)列
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
PostingThreadState() {
}
}
}
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<>();//事件隊(duì)列
boolean isPosting;//是否正在分發(fā)中
boolean isMainThread;//是否在主線程
Subscription subscription;
Object event;//事件
boolean canceled;//取消
}
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};//currentPostingThreadState 是一個(gè)線程局部變量
/** Posts the given event to the event bus. */
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();//獲取當(dāng)前線程事件分發(fā)狀態(tài)
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);//添加到事務(wù)隊(duì)列中
if (!postingState.isPosting) {//如果當(dāng)前不是在分發(fā)狀態(tài),則進(jìn)入分發(fā)狀態(tài)
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
//編譯事件隊(duì)列,逐一進(jìn)行分發(fā)
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
EventBus#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));
}
}
}
EventBus#postSingleEventForEventType,
//查找 事件所對(duì)應(yīng)的訂閱者列表,然后迭代列表,切換到目標(biāo)線程執(zhí)行
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);//查找 事件所對(duì)應(yīng)的訂閱者列表
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
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;
}
2.切換到指定線程
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://后臺(tái)線程
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC://新建一個(gè)子線程處理
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
BACKGROUND
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);//包裝為一個(gè) PendingPost
synchronized (this) {//加鎖,進(jìn)入同步塊
queue.enqueue(pendingPost);//添加到隊(duì)列中
if (!executorRunning) {//如果現(xiàn)在沒(méi)有在執(zhí)行 后臺(tái)任務(wù),則獲取一個(gè)新線程執(zhí)行任務(wù)
executorRunning = true;
eventBus.getExecutorService().execute(this);
}
}
}
@Override
public void run() {
try {
try {
while (true) {//循環(huán),直到隊(duì)列為空
PendingPost pendingPost = queue.poll(1000);//獲取 PendingPost,最多等到 1 秒
if (pendingPost == null) {
//前面取不到 PendingPost,下面進(jìn)行雙重校驗(yàn)檢查
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();//
if (pendingPost == null) {
//隊(duì)列確實(shí)為空,停止運(yùn)行
executorRunning = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);//通過(guò)反射調(diào)用相應(yīng)的方法
}
} catch (InterruptedException e) {
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
MAIN
mainThreadPoster.enqueue(subscription, event);
HandlerPoster#enqueue
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {//加鎖
queue.enqueue(pendingPost);//加入 pendingQueue 中。該隊(duì)列會(huì)在 handleMessage 方法中調(diào)用
if (!handlerActive) {//如果當(dāng)前 handler 不是處于活躍狀態(tài),則退出
handlerActive = true;
if (!sendMessage(obtainMessage())) {//發(fā)送信息,提示
throw new EventBusException("Could not send handler message");
}
}
}
}
org.greenrobot.eventbus.HandlerPoster#handleMessage
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
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;
}
}
在 handleMessage 方法內(nèi)部停留時(shí)間不能大于 10 毫秒,從
MAIN_ORDERED
新增加的模式,但是當(dāng)前版本的實(shí)現(xiàn)還不完善。
ASYN
將事件添加 cachedThreadPool 中執(zhí)行(如果當(dāng)前有空閑線程,則復(fù)用空閑線程,如果沒(méi)有就創(chuàng)建新線程)
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
queue.enqueue(pendingPost);
eventBus.getExecutorService().execute(this);//放到默認(rèn)的 cachedThreaPool 中執(zhí)行
}
@Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
3.反射調(diào)用訂閱方法
org.greenrobot.eventbus.EventBus#invokeSubscriber(org.greenrobot.eventbus.PendingPost)
void invokeSubscriber(PendingPost pendingPost) {
Object event = pendingPost.event;
Subscription subscription = pendingPost.subscription;
PendingPost.releasePendingPost(pendingPost);
if (subscription.active) {
invokeSubscriber(subscription, event);
}
}
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);
}
}
切換到指定線程之后,通過(guò)反射的方法,調(diào)用訂閱方法。
四、總結(jié)
EventBus 的實(shí)現(xiàn)原理可以歸結(jié)為以下三點(diǎn)
- 注冊(cè)—>掃描訂閱方法,添加到訂閱方法列表中
- 發(fā)送事件—>根據(jù)事件的類型,遍歷方法列表,反射調(diào)用訂閱方法
- 解注冊(cè)—>從訂閱方法列表中移除相應(yīng)的訂閱方法
EventBus 雖然不是標(biāo)準(zhǔn)的觀察者模式的實(shí)現(xiàn), 但是它的整體就是一個(gè)發(fā)布 / 訂閱框架, 也擁有觀察者模式的優(yōu)點(diǎn), 比如: 發(fā)布者和訂閱者的解耦。
五、參考資料與學(xué)習(xí)資源推薦
由于本人水平有限,可能出于誤解或者筆誤難免出錯(cuò),如果發(fā)現(xiàn)有問(wèn)題或者對(duì)文中內(nèi)容存在疑問(wèn)歡迎在下面評(píng)論區(qū)告訴我。謝謝!