EventBus 是一款在 Android 開發(fā)中使用的發(fā)布/訂閱事件總線框架缔俄,基于觀察者模式弛秋,將事件的接收者和發(fā)送者分開,簡(jiǎn)化了組件之間的通信俐载,使用簡(jiǎn)單蟹略、效率高、體積卸粲丁挖炬!下邊是官方的 EventBus 原理圖:
EventBus 的用法可以參考官網(wǎng),這里不做過(guò)多的說(shuō)明状婶。本文主要是從 EventBus 使用的方式入手意敛,來(lái)分析 EventBus 背后的實(shí)現(xiàn)原理,以下內(nèi)容基于eventbus:3.1.1版本太抓,主要包括如下幾個(gè)方面的內(nèi)容:
- Subscribe注解
- 注冊(cè)事件訂閱方法
- 取消注冊(cè)
- 發(fā)送事件
- 事件處理
- 粘性事件
- Subscriber Index
- 核心流程梳理
一空闲、Subscribe注解
EventBus3.0 開始用Subscribe
注解配置事件訂閱方法,不再使用方法名了走敌,例如:
@Subscribe
public void handleEvent(String event) {
// do something
}
其中事件類型可以是 Java 中已有的類型或者我們自定義的類型碴倾。
具體看下Subscribe
注解的實(shí)現(xiàn):
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
// 指定事件訂閱方法的線程模式,即在那個(gè)線程執(zhí)行事件訂閱方法處理事件掉丽,默認(rèn)為POSTING
ThreadMode threadMode() default ThreadMode.POSTING;
// 是否支持粘性事件跌榔,默認(rèn)為false
boolean sticky() default false;
// 指定事件訂閱方法的優(yōu)先級(jí),默認(rèn)為0捶障,如果多個(gè)事件訂閱方法可以接收相同事件的僧须,則優(yōu)先級(jí)高的先接收到事件
int priority() default 0;
}
所以在使用Subscribe
注解時(shí)可以根據(jù)需求指定threadMode
、sticky
项炼、priority
三個(gè)屬性担平。
其中threadMode
屬性有如下幾個(gè)可選值:
- ThreadMode.POSTING示绊,默認(rèn)的線程模式,在那個(gè)線程發(fā)送事件就在對(duì)應(yīng)線程處理事件暂论,避免了線程切換面褐,效率高。
- ThreadMode.MAIN取胎,如在主線程(UI線程)發(fā)送事件展哭,則直接在主線程處理事件;如果在子線程發(fā)送事件闻蛀,則先將事件入隊(duì)列匪傍,然后通過(guò) Handler 切換到主線程,依次處理事件觉痛。
- ThreadMode.MAIN_ORDERED役衡,無(wú)論在那個(gè)線程發(fā)送事件,都先將事件入隊(duì)列秧饮,然后通過(guò) Handler 切換到主線程映挂,依次處理事件。
- ThreadMode.BACKGROUND盗尸,如果在主線程發(fā)送事件柑船,則先將事件入隊(duì)列,然后通過(guò)線程池依次處理事件泼各;如果在子線程發(fā)送事件鞍时,則直接在發(fā)送事件的線程處理事件。
- ThreadMode.ASYNC扣蜻,無(wú)論在那個(gè)線程發(fā)送事件逆巍,都將事件入隊(duì)列,然后通過(guò)線程池處理莽使。
二锐极、注冊(cè)事件訂閱方法
注冊(cè)事件的方式如下:
EventBus.getDefault().register(this);
其中getDefault()
是一個(gè)單例方法,保證當(dāng)前只有一個(gè)EventBus
實(shí)例:
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
繼續(xù)看new EventBus()
做了些什么:
public EventBus() {
this(DEFAULT_BUILDER);
}
在這里又調(diào)用了EventBus
的另一個(gè)構(gòu)造函數(shù)來(lái)完成它相關(guān)屬性的初始化:
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;
}
DEFAULT_BUILDER
就是一個(gè)默認(rèn)的EventBusBuilder:
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
如果有需要的話芳肌,我們也可以通過(guò)配置EventBusBuilder
來(lái)更改EventBus
的屬性灵再,例如用如下方式注冊(cè)事件:
EventBus.builder()
.eventInheritance(false)
.logSubscriberExceptions(false)
.build()
.register(this);
有了EventBus
的實(shí)例就可以進(jìn)行注冊(cè)了:
public void register(Object subscriber) {
// 得到當(dāng)前要注冊(cè)類的Class對(duì)象
Class<?> subscriberClass = subscriber.getClass();
// 根據(jù)Class查找當(dāng)前類中訂閱了事件的方法集合,即使用了Subscribe注解亿笤、有public修飾符翎迁、一個(gè)參數(shù)的方法
// SubscriberMethod類主要封裝了符合條件方法的相關(guān)信息:
// Method對(duì)象、線程模式净薛、事件類型汪榔、優(yōu)先級(jí)、是否是粘性事等
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
// 循環(huán)遍歷訂閱了事件的方法集合肃拜,以完成注冊(cè)
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
可以看到register()
方法主要分為查找和注冊(cè)兩部分痴腌,首先來(lái)看查找的過(guò)程雌团,從findSubscriberMethods()
開始:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
// METHOD_CACHE是一個(gè)ConcurrentHashMap,直接保存了subscriberClass和對(duì)應(yīng)SubscriberMethod的集合士聪,以提高注冊(cè)效率辱姨,賦值重復(fù)查找。
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
// 由于使用了默認(rèn)的EventBusBuilder戚嗅,則ignoreGeneratedIndex屬性默認(rèn)為false,即是否忽略注解生成器
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
// 如果對(duì)應(yīng)類中沒(méi)有符合條件的方法枢舶,則拋出異常
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()
流程很清晰懦胞,即先從緩存中查找,如果找到則直接返回凉泄,否則去做下一步的查找過(guò)程躏尉,然后緩存查找到的集合,根據(jù)上邊的注釋可知findUsingInfo()
方法會(huì)被調(diào)用:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
// 初始狀態(tài)下findState.clazz就是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 {
// 通過(guò)反射查找訂閱事件的方法
findUsingReflectionInSingleClass(findState);
}
// 修改findState.clazz為subscriberClass的父類Class后众,即需要遍歷父類
findState.moveToSuperclass();
}
// 查找到的方法保存在了FindState實(shí)例的subscriberMethods集合中胀糜。
// 使用subscriberMethods構(gòu)建一個(gè)新的List<SubscriberMethod>
// 釋放掉findState
return getMethodsAndRelease(findState);
}
findUsingInfo()
方法會(huì)在當(dāng)前要注冊(cè)的類以及其父類中查找訂閱事件的方法,這里出現(xiàn)了一個(gè)FindState
類蒂誉,它是SubscriberMethodFinder
的內(nèi)部類教藻,用來(lái)輔助查找訂閱事件的方法,具體的查找過(guò)程在findUsingReflectionInSingleClass()
方法右锨,它主要通過(guò)反射查找訂閱事件的方法:
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;
}
// 循環(huán)遍歷當(dāng)前類的方法,篩選出符合條件的
for (Method method : methods) {
// 獲得方法的修飾符
int modifiers = method.getModifiers();
// 如果是public類型,但非abstract素征、static等
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
// 獲得當(dāng)前方法所有參數(shù)的類型
Class<?>[] parameterTypes = method.getParameterTypes();
// 如果當(dāng)前方法只有一個(gè)參數(shù)
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
// 如果當(dāng)前方法使用了Subscribe注解
if (subscribeAnnotation != null) {
// 得到該參數(shù)的類型
Class<?> eventType = parameterTypes[0];
// checkAdd()方法用來(lái)判斷FindState的anyMethodByEventType map是否已經(jīng)添加過(guò)以當(dāng)前eventType為key的鍵值對(duì)急凰,沒(méi)添加過(guò)則返回true
if (findState.checkAdd(method, eventType)) {
// 得到Subscribe注解的threadMode屬性值,即線程模式
ThreadMode threadMode = subscribeAnnotation.threadMode();
// 創(chuàng)建一個(gè)SubscriberMethod對(duì)象蹂窖,并添加到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");
}
}
}
到此register()
方法中findSubscriberMethods()
流程就分析完了轧抗,我們已經(jīng)找到了當(dāng)前注冊(cè)類及其父類中訂閱事件的方法的集合。接下來(lái)分析具體的注冊(cè)流程瞬测,即register()
中的subscribe()
方法:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 得到當(dāng)前訂閱了事件的方法的參數(shù)類型
Class<?> eventType = subscriberMethod.eventType;
// Subscription類保存了要注冊(cè)的類對(duì)象以及當(dāng)前的subscriberMethod
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// subscriptionsByEventType是一個(gè)HashMap横媚,保存了以eventType為key,Subscription對(duì)象集合為value的鍵值對(duì)
// 先查找subscriptionsByEventType是否存在以當(dāng)前eventType為key的值
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
// 如果不存在,則創(chuàng)建一個(gè)subscriptions涣楷,并保存到subscriptionsByEventType
if (subscriptions == null) {
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對(duì)象到subscriptions中
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
// typesBySubscribere也是一個(gè)HashMap分唾,保存了以當(dāng)前要注冊(cè)類的對(duì)象為key,注冊(cè)類中訂閱事件的方法的參數(shù)類型的集合為value的鍵值對(duì)
// 查找是否存在對(duì)應(yīng)的參數(shù)類型集合
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
// 不存在則創(chuàng)建一個(gè)subscribedEvents狮斗,并保存到typesBySubscriber
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
// 保存當(dāng)前訂閱了事件的方法的參數(shù)類型
subscribedEvents.add(eventType);
// 粘性事件相關(guān)的绽乔,后邊具體分析
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);
}
}
}
這就是注冊(cè)的核心流程,所以subscribe()
方法主要是得到了subscriptionsByEventType
碳褒、typesBySubscriber
兩個(gè) HashMap折砸。我們?cè)诎l(fā)送事件的時(shí)候要用到subscriptionsByEventType
看疗,完成事件的處理。當(dāng)取消 EventBus 注冊(cè)的時(shí)候要用到typesBySubscriber
睦授、subscriptionsByEventType
两芳,完成相關(guān)資源的釋放。
三去枷、取消注冊(cè)
接下來(lái)看怖辆,EventBus 如何取消注冊(cè):
EventBus.getDefault().unregister(this);
核心的方法就是unregister()
:
public synchronized void unregister(Object subscriber) {
// 得到當(dāng)前注冊(cè)類對(duì)象 對(duì)應(yīng)的 訂閱事件方法的參數(shù)類型 的集合
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
// 遍歷參數(shù)類型集合,釋放之前緩存的當(dāng)前類中的Subscription
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
// 刪除以subscriber為key的鍵值對(duì)
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
內(nèi)容很簡(jiǎn)單删顶,繼續(xù)看unsubscribeByEventType()
方法:
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
// 得到當(dāng)前參數(shù)類型對(duì)應(yīng)的Subscription集合
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
// 遍歷Subscription集合
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
// 如果當(dāng)前subscription對(duì)象對(duì)應(yīng)的注冊(cè)類對(duì)象 和 要取消注冊(cè)的注冊(cè)類對(duì)象相同竖螃,則刪除當(dāng)前subscription對(duì)象
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
所以在unregister()
方法中,釋放了typesBySubscriber
逗余、subscriptionsByEventType
中緩存的資源特咆。
四、發(fā)送事件
當(dāng)發(fā)送一個(gè)事件的時(shí)候录粱,我們可以通過(guò)如下方式:
EventBus.getDefault().post("Hello World!")
可以看到腻格,發(fā)送事件就是通過(guò)post()
方法完成的:
public void post(Object event) {
// currentPostingThreadState是一個(gè)PostingThreadState類型的ThreadLocal
// PostingThreadState類保存了事件隊(duì)列和線程模式等信息
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
// 將要發(fā)送的事件添加到事件隊(duì)列
eventQueue.add(event);
// isPosting默認(rèn)為false
if (!postingState.isPosting) {
// 是否為主線程
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
// 遍歷事件隊(duì)列
while (!eventQueue.isEmpty()) {
// 發(fā)送單個(gè)事件
// eventQueue.remove(0),從事件隊(duì)列移除事件
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
所以post()
方法先將發(fā)送的事件保存的事件隊(duì)列啥繁,然后通過(guò)循環(huán)出隊(duì)列菜职,將事件交給postSingleEvent()
方法處理:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
// eventInheritance默認(rèn)為true,表示是否向上查找事件的父類
if (eventInheritance) {
// 查找當(dāng)前事件類型的Class旗闽,連同當(dāng)前事件類型的Class保存到集合
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
// 遍歷Class集合些楣,繼續(xù)處理事件
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()
方法中,根據(jù)eventInheritance
屬性宪睹,決定是否向上遍歷事件的父類型愁茁,然后用postSingleEventForEventType()
方法進(jìn)一步處理事件:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
// 獲取事件類型對(duì)應(yīng)的Subscription集合
subscriptions = subscriptionsByEventType.get(eventClass);
}
// 如果已訂閱了對(duì)應(yīng)類型的事件
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
// 記錄事件
postingState.event = event;
// 記錄對(duì)應(yīng)的subscription
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;
}
postSingleEventForEventType()
方法核心就是遍歷發(fā)送的事件類型對(duì)應(yīng)的Subscription集合,然后調(diào)用postToSubscription()
方法處理事件亭病。
五鹅很、處理事件
接著上邊的繼續(xù)分析,postToSubscription()
內(nèi)部會(huì)根據(jù)訂閱事件方法的線程模式罪帖,間接或直接的以發(fā)送的事件為參數(shù)促煮,通過(guò)反射執(zhí)行訂閱事件的方法。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
// 判斷訂閱事件方法的線程模式
switch (subscription.subscriberMethod.threadMode) {
// 默認(rèn)的線程模式整袁,在那個(gè)線程發(fā)送事件就在那個(gè)線程處理事件
case POSTING:
invokeSubscriber(subscription, event);
break;
// 在主線程處理事件
case MAIN:
// 如果在主線程發(fā)送事件菠齿,則直接在主線程通過(guò)反射處理事件
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
// 如果是在子線程發(fā)送事件,則將事件入隊(duì)列坐昙,通過(guò)Handler切換到主線程執(zhí)行處理事件
// mainThreadPoster 不為空
mainThreadPoster.enqueue(subscription, event);
}
break;
// 無(wú)論在那個(gè)線程發(fā)送事件绳匀,都先將事件入隊(duì)列,然后通過(guò) Handler 切換到主線程,依次處理事件疾棵。
// mainThreadPoster 不為空
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND:
// 如果在主線程發(fā)送事件戈钢,則先將事件入隊(duì)列,然后通過(guò)線程池依次處理事件
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
// 如果在子線程發(fā)送事件是尔,則直接在發(fā)送事件的線程通過(guò)反射處理事件
invokeSubscriber(subscription, event);
}
break;
// 無(wú)論在那個(gè)線程發(fā)送事件殉了,都將事件入隊(duì)列,然后通過(guò)線程池處理拟枚。
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
可以看到薪铜,postToSubscription()
方法就是根據(jù)訂閱事件方法的線程模式、以及發(fā)送事件的線程來(lái)判斷如何處理事件恩溅,至于處理方式主要有兩種:
一種是在相應(yīng)線程直接通過(guò)invokeSubscriber()
方法痕囱,用反射來(lái)執(zhí)行訂閱事件的方法,這樣發(fā)送出去的事件就被訂閱者接收并做相應(yīng)處理了:
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);
}
}
另外一種是先將事件入隊(duì)列(其實(shí)底層是一個(gè)List)暴匠,然后做進(jìn)一步處理,我們以mainThreadPoster.enqueue(subscription, event)
為例簡(jiǎn)單的分析下傻粘,其中mainThreadPoster
是HandlerPoster
類的一個(gè)實(shí)例每窖,來(lái)看該類的主要實(shí)現(xiàn):
public class HandlerPoster extends Handler implements Poster {
private final PendingPostQueue queue;
private boolean handlerActive;
......
public void enqueue(Subscription subscription, Object event) {
// 用subscription和event封裝一個(gè)PendingPost對(duì)象
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
// 入隊(duì)列
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
// 發(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();
// 死循環(huán)遍歷隊(duì)列
while (true) {
// 出隊(duì)列
PendingPost pendingPost = queue.poll();
......
// 進(jìn)一步處理pendingPost
eventBus.invokeSubscriber(pendingPost);
......
}
} finally {
handlerActive = rescheduled;
}
}
}
所以HandlerPoster
的enqueue()
方法主要就是將subscription
窒典、event
對(duì)象封裝成一個(gè)PendingPost
對(duì)象,然后保存到隊(duì)列里稽莉,之后通過(guò)Handler
切換到主線程瀑志,在handleMessage()
方法將中將PendingPost
對(duì)象循環(huán)出隊(duì)列,交給invokeSubscriber()
方法進(jìn)一步處理:
void invokeSubscriber(PendingPost pendingPost) {
Object event = pendingPost.event;
Subscription subscription = pendingPost.subscription;
// 釋放pendingPost引用的資源
PendingPost.releasePendingPost(pendingPost);
if (subscription.active) {
// 用反射來(lái)執(zhí)行訂閱事件的方法
invokeSubscriber(subscription, event);
}
}
這個(gè)方法很簡(jiǎn)單污秆,主要就是從pendingPost
中取出之前保存的event
劈猪、subscription
,然后用反射來(lái)執(zhí)行訂閱事件的方法良拼,又回到了第一種處理方式战得。所以mainThreadPoster.enqueue(subscription, event)
的核心就是先將將事件入隊(duì)列,然后通過(guò)Handler從子線程切換到主線程中去處理事件庸推。
backgroundPoster.enqueue()
和asyncPoster.enqueue
也類似常侦,內(nèi)部都是先將事件入隊(duì)列,然后再出隊(duì)列贬媒,但是會(huì)通過(guò)線程池去進(jìn)一步處理事件聋亡。
六、粘性事件
一般情況际乘,我們使用 EventBus 都是準(zhǔn)備好訂閱事件的方法坡倔,然后注冊(cè)事件,最后在發(fā)送事件,即要先有事件的接收者致讥。但粘性事件卻恰恰相反仅仆,我們可以先發(fā)送事件,后續(xù)再準(zhǔn)備訂閱事件的方法垢袱、注冊(cè)事件墓拜。
由于這種差異,我們分析粘性事件原理時(shí)请契,先從事件發(fā)送開始咳榜,發(fā)送一個(gè)粘性事件通過(guò)如下方式:
EventBus.getDefault().postSticky("Hello World!");
來(lái)看postSticky()
方法是如何實(shí)現(xiàn)的:
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
post(event);
}
postSticky()
方法主要做了兩件事,先將事件類型和對(duì)應(yīng)事件保存到stickyEvents
中爽锥,方便后續(xù)使用涌韩;然后執(zhí)行post(event)
繼續(xù)發(fā)送事件,這個(gè)post()
方法就是之前發(fā)送的post()
方法氯夷。所以臣樱,如果在發(fā)送粘性事件前,已經(jīng)有了對(duì)應(yīng)類型事件的訂閱者腮考,及時(shí)它是非粘性的雇毫,依然可以接收到發(fā)送出的粘性事件。
發(fā)送完粘性事件后踩蔚,再準(zhǔn)備訂閱粘性事件的方法棚放,并完成注冊(cè)。核心的注冊(cè)事件流程還是我們之前的register()
方法中的subscribe()
方法馅闽,前邊分析subscribe()
方法時(shí)飘蚯,有一段沒(méi)有分析的代碼,就是用來(lái)處理粘性事件的:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
......
......
......
// 如果當(dāng)前訂閱事件的方法的Subscribe注解的sticky屬性為true福也,即該方法可接受粘性事件
if (subscriberMethod.sticky) {
// 默認(rèn)為true局骤,表示是否向上查找事件的父類
if (eventInheritance) {
// stickyEvents就是發(fā)送粘性事件時(shí),保存了事件類型和對(duì)應(yīng)事件
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)) {
// 獲得對(duì)應(yīng)的事件
Object stickyEvent = entry.getValue();
// 處理粘性事件
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
可以看到暴凑,處理粘性事件就是在 EventBus 注冊(cè)時(shí)庄涡,遍歷stickyEvents
,如果當(dāng)前要注冊(cè)的事件訂閱方法是粘性的搬设,并且該方法接收的事件類型和stickyEvents
中某個(gè)事件類型相同或者是其父類穴店,則取出stickyEvents
中對(duì)應(yīng)事件類型的具體事件,做進(jìn)一步處理拿穴。繼續(xù)看checkPostStickyEventToSubscription()
處理方法:
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
最終還是通過(guò)postToSubscription()
方法完成粘性事件的處理泣洞,這就是粘性事件的整個(gè)處理流程。
七默色、Subscriber Index
回顧之前分析的 EventBus 注冊(cè)事件流程球凰,主要是在項(xiàng)目運(yùn)行時(shí)通過(guò)反射來(lái)查找訂事件的方法信息,這也是默認(rèn)的實(shí)現(xiàn),如果項(xiàng)目中有大量的訂閱事件的方法呕诉,必然會(huì)對(duì)項(xiàng)目運(yùn)行時(shí)的性能產(chǎn)生影響缘厢。其實(shí)除了在項(xiàng)目運(yùn)行時(shí)通過(guò)反射查找訂閱事件的方法信息,EventBus 還提供了在項(xiàng)目編譯時(shí)通過(guò)注解處理器查找訂閱事件方法信息的方式甩挫,生成一個(gè)輔助的索引類來(lái)保存這些信息贴硫,這個(gè)索引類就是Subscriber Index,其實(shí)和 ButterKnife 的原理類似伊者。
要在項(xiàng)目編譯時(shí)查找訂閱事件的方法信息英遭,首先要在 app 的 build.gradle 中加入如下配置:
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
// 根據(jù)項(xiàng)目實(shí)際情況,指定輔助索引類的名稱和包名
arguments = [ eventBusIndex : 'com.shh.sometest.MyEventBusIndex' ]
}
}
}
}
dependencies {
compile 'org.greenrobot:eventbus:3.1.1'
// 引入注解處理器
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}
然后在項(xiàng)目的 Application 中添加如下配置亦渗,以生成一個(gè)默認(rèn)的 EventBus 單例:
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
之后的用法就和我們平時(shí)使用 EventBus 一樣了挖诸。當(dāng)項(xiàng)目編譯時(shí)會(huì)在生成對(duì)應(yīng)的MyEventBusIndex
類:
對(duì)應(yīng)的源碼如下:
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("changeText", String.class),
}));
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}
其中SUBSCRIBER_INDEX
是一個(gè)HashMap,保存了當(dāng)前注冊(cè)類的 Class 類型和其中事件訂閱方法的信息法精。
接下來(lái)分析下使用 Subscriber Index 時(shí) EventBus 的注冊(cè)流程多律,我們先分析:
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
首先創(chuàng)建一個(gè)EventBusBuilder
,然后通過(guò)addIndex()
方法添加索引類的實(shí)例:
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if (subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
即把生成的索引類的實(shí)例保存在subscriberInfoIndexes
集合中搂蜓,然后用installDefaultEventBus()
創(chuàng)建默認(rèn)的 EventBus實(shí)例:
public EventBus installDefaultEventBus() {
synchronized (EventBus.class) {
if (EventBus.defaultInstance != null) {
throw new EventBusException("Default instance already exists." +
" It may be only set once before it's used the first time to ensure consistent behavior.");
}
EventBus.defaultInstance = build();
return EventBus.defaultInstance;
}
}
public EventBus build() {
// this 代表當(dāng)前EventBusBuilder對(duì)象
return new EventBus(this);
}
即用當(dāng)前EventBusBuilder
對(duì)象創(chuàng)建一個(gè) EventBus 實(shí)例狼荞,這樣我們通過(guò)EventBusBuilder
配置的 Subscriber Index 也就傳遞到了EventBus實(shí)例中,然后賦值給EventBus的 defaultInstance
成員變量洛勉。之前我們?cè)诜治?EventBus 的getDefault()
方法時(shí)已經(jīng)見到了defaultInstance
:
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
所以在 Application 中生成了 EventBus 的默認(rèn)單例,這樣就保證了在項(xiàng)目其它地方執(zhí)行EventBus.getDefault()
就能得到唯一的 EventBus 實(shí)例如迟!之前在分析注冊(cè)流程時(shí)有一個(gè)
方法findUsingInfo()
:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
// 查找SubscriberInfo
findState.subscriberInfo = getSubscriberInfo(findState);
// 條件成立
if (findState.subscriberInfo != null) {
// 獲得當(dāng)前注冊(cè)類中所有訂閱了事件的方法
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
// findState.checkAdd()之前已經(jīng)分析過(guò)了收毫,即是否在FindState的anyMethodByEventType已經(jīng)添加過(guò)以當(dāng)前eventType為key的鍵值對(duì),沒(méi)添加過(guò)返回true
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
// 將subscriberMethod對(duì)象添加到subscriberMethods集合
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
由于我們現(xiàn)在使用了 Subscriber Index 所以不會(huì)通過(guò)findUsingReflectionInSingleClass()
來(lái)反射解析訂閱事件的方法殷勘。我們重點(diǎn)來(lái)看getSubscriberInfo()
都做了些什么:
private SubscriberInfo getSubscriberInfo(FindState findState) {
// 該條件不成立
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
// 該條件成立
if (subscriberInfoIndexes != null) {
// 遍歷索引類實(shí)例集合
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
// 根據(jù)注冊(cè)類的 Class 類查找SubscriberInfo
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
subscriberInfoIndexes
就是在前邊addIndex()
方法中創(chuàng)建的此再,保存了項(xiàng)目中的索引類實(shí)例,即MyEventBusIndex
的實(shí)例玲销,繼續(xù)看索引類的getSubscriberInfo()
方法输拇,來(lái)到了MyEventBusIndex
類中:
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
即根據(jù)注冊(cè)類的 Class 類型從 SUBSCRIBER_INDEX
查找對(duì)應(yīng)的SubscriberInfo
,如果我們?cè)谧?cè)類中定義了訂閱事件的方法贤斜,則 info
不為空策吠,進(jìn)而上邊findUsingInfo()
方法中findState.subscriberInfo != null
成立,到這里主要的內(nèi)容就分析完了瘩绒,其它的和之前的注冊(cè)流程一樣猴抹。
所以 Subscriber Index 的核心就是項(xiàng)目編譯時(shí)使用注解處理器生成保存事件訂閱方法信息的索引類,然后項(xiàng)目運(yùn)行時(shí)將索引類實(shí)例設(shè)置到 EventBus 中锁荔,這樣當(dāng)注冊(cè) EventBus 時(shí)蟀给,從索引類取出當(dāng)前注冊(cè)類對(duì)應(yīng)的事件訂閱方法信息,以完成最終的注冊(cè),避免了運(yùn)行時(shí)反射處理的過(guò)程跋理,所以在性能上會(huì)有質(zhì)的提高择克。項(xiàng)目中可以根據(jù)實(shí)際的需求決定是否使用 Subscriber Index。
八前普、小結(jié)
結(jié)合上邊的分析肚邢,我們可以總結(jié)出register
、post
汁政、unregister
的核心流程:
到這里 EventBus 幾個(gè)重要的流程就分析完了道偷,整體的設(shè)計(jì)思路還是值得我們學(xué)習(xí)的。和 Android 自帶的廣播相比记劈,使用簡(jiǎn)單勺鸦、同時(shí)又兼顧了性能。但如果在項(xiàng)目中濫用的話目木,各種邏輯交叉在一起换途,也可能會(huì)給后期的維護(hù)帶來(lái)困難。