這篇文章將會(huì)為大家梳理一下EventBus的基本流程伸但,本人使用的版本號為3.1.1血筑,為了方便閱讀,文章中的源碼部分將省略部分有關(guān)異常捕獲與日志相關(guān)代碼赃绊。
使用示例
首先,按照官方的文檔來看看一個(gè)最簡單的EventBus示例是什么樣的:
第一步:定義消息實(shí)體類
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
第二步:使用注解創(chuàng)建訂閱方法
@Subscribe
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
第三步:注冊與注銷訂閱(注意與生命周期的綁定)
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
第四步:發(fā)送通知消息
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
這樣羡榴,一個(gè)簡單的Demo就完成了碧查,本文將以這個(gè)Demo為基礎(chǔ),分析訂閱事件從注冊到接收并執(zhí)行都是如何實(shí)現(xiàn)的校仑。
1.EventBus.getDefault().register(this);
EventBus.getDefault()
static volatile EventBus defaultInstance;
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
獲取EventBus的單例對象么夫,很標(biāo)準(zhǔn)的單例模式,就不細(xì)說了肤视。
EventBus.register(Object subscriber)
public void register(Object subscriber) {
//獲取注冊訂閱的類的Class對象
Class<?> subscriberClass = subscriber.getClass();
//查找Class中帶有Subscribe注解的方法列表
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
SubscriberMethodFinder.findSubscriberMethods(Class<?> subscriberClass)
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//METHOD_CACHE為ConcurrentHashMap<Class<?>, List<SubscriberMethod>>,作為方法解析的緩存使用
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
//如果取出的SubscriberMethod不為null涉枫,則說明該類已被加載過邢滑,那么跳過解析的步驟,直接返回上次解析的結(jié)果
if (subscriberMethods != null) {
return subscriberMethods;
}
//ignoreGeneratedIndex用來設(shè)置是否忽略使用Subscriber Index來幫助注解解析愿汰,默認(rèn)設(shè)置為false
//Subscriber Index為EventBus3.0中出現(xiàn)的新特性困后,在build期間生成,以此增加注解解析的性能
//關(guān)于Subscriber Index的更多信息可以參考官方文檔 http://greenrobot.org/eventbus/documentation/subscriber-index/
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
//如果類中沒有找到Subscriber注解的方法衬廷,拋出異常
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//將Subscriber注解的方法放入緩存并返回
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
從這個(gè)方法可以看出EventBus會(huì)將注冊的訂閱事件以Class對象為key摇予,訂閱方法的Method對象為value存入METHOD_CACHE緩存中,避免同一個(gè)類多次注冊訂閱時(shí)重復(fù)解析的問題吗跋,提升解析的性能侧戴。
在注解解析方面,EventBus提供了傳統(tǒng)的反射解析與使用Subscriber Index兩種方式跌宛,下面將主要對反射解析方式分別進(jìn)行分析
SubscriberMethodFinder.findUsingReflection(Class<?> subscriberClass)
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
//通過FindState對象池創(chuàng)建FindState對象
FindState findState = prepareFindState();
//初始化FindState對象
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//解析這個(gè)類中帶有Subscribe注解的方法
findUsingReflectionInSingleClass(findState);
//向父類進(jìn)行遍歷
findState.moveToSuperclass();
}
//獲取解析出的方法酗宋,將findState重置并放回對象池中
return getMethodsAndRelease(findState);
}
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
//getDeclaredMethods方法只會(huì)獲取到該類所定義的方法,而getMethods方法會(huì)獲取包括這個(gè)類的父類與接口中的所有方法
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
// 使用getMethods就已經(jīng)獲得了其父類的所有方法疆拘,所以將skipSuperClasses標(biāo)志位設(shè)置為true蜕猫,在后續(xù)過程中不對其父類進(jìn)行解析
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
//Subscribe注解的方法不應(yīng)為私有且不應(yīng)為抽象,靜態(tài)等
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
//Subscribe注解的方法應(yīng)有且只有一個(gè)參數(shù)
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
//調(diào)用checkAdd方法判斷是否將訂閱事件添加進(jìn)訂閱列表中
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
}
}
}
}
在這段代碼中出現(xiàn)了一個(gè)FindState類對象哎迄,其中含有解析相關(guān)的配置回右,解析出的方法等等,整個(gè)解析的過程都是圍繞著這個(gè)對象進(jìn)行處理漱挚。
FindState.checkAdd
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
boolean checkAdd(Method method, Class<?> eventType) {
// 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
// Usually a subscriber doesn't have methods listening to the same event type.
Object existing = anyMethodByEventType.put(eventType, method);
// 第一級判斷翔烁,如果existing為null,則代表該eventType為首次添加棱烂,直接返回true
if (existing == null) {
return true;
} else {
// 對于這段代碼租漂,作者這么處理的用意我不是很明白,希望能有人能為我解答一下
if (existing instanceof Method) {
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
// Paranoia check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" the existing Method
// 該方法只為了將一個(gè)不是Method的對象放入,避免下一次再次進(jìn)入此邏輯
anyMethodByEventType.put(eventType, this);
}
return checkAddWithMethodSignature(method, eventType);
}
}
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
//使用方法名與參數(shù)類型名生成該方法簽名
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class<?> methodClass = method.getDeclaringClass();
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
// 如果有現(xiàn)方法的父類方法已注冊過事件,則繼續(xù)添加哩治,使用現(xiàn)方法進(jìn)行覆蓋
return true;
} else {
// Revert the put, old class is further down the class hierarchy
// 撤銷之前的put操作秃踩,并返回false
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
這部分代碼的作用主要有兩點(diǎn):
- 當(dāng)一個(gè)類重載了父類的一個(gè)訂閱方法,在向上級父類遍歷時(shí)跳過父類中的這個(gè)方法的訂閱业筏,也就是說以子類的訂閱方法為準(zhǔn)
- 允許一個(gè)類有多個(gè)方法名不同的方法對同個(gè)事件進(jìn)行訂閱
值得一提的是關(guān)于methodClassOld.isAssignableFrom(methodClass)這一句代碼憔杨,有文章提到因?yàn)橹暗拇a中會(huì)使用Class.getMethods()方法,會(huì)得到這個(gè)類以及父類的public方法蒜胖,所以這句代碼的結(jié)果可能為true消别,但是經(jīng)我測試,如果父類方法被子類重載台谢,那么getMethods方法得到的只會(huì)有子類的重載方法一個(gè)寻狂,父類的方法并不會(huì)出現(xiàn),所以從這方面來說朋沮,這句代碼依然只能是false蛇券。
EventBus.register(Object subscriber, SubscriberMethod subscriberMethod)
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
//如果該事件沒有注冊過,則將訂閱事件添加進(jìn)訂閱列表中
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
//同一對象內(nèi)訂閱事件不能重復(fù)注冊
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
//根據(jù)訂閱事件的優(yōu)先級將事件加入訂閱列表中
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
//將參數(shù)類型以訂閱者對象為key樊拓,參數(shù)類型列表為value保存進(jìn)一個(gè)Map中
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//對于一般的事件的注冊纠亚,到這里就已經(jīng)完成了
//-----------------------------------------------------------------------------
//針對粘性事件訂閱者的處理
if (subscriberMethod.sticky) {
//eventInheritance:是否支持事件繼承,默認(rèn)為true筋夏。當(dāng)為true時(shí)蒂胞,post一個(gè)事件A時(shí),若A是B的父類条篷,訂閱B的訂閱者也能接收到事件骗随。
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>).
//遍歷所有注冊的粘性事件進(jìn)行遍歷,如果一個(gè)事件為現(xiàn)事件的子類赴叹,則將該事件發(fā)送出去
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 {
//發(fā)送訂閱的粘性事件
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
到現(xiàn)在為止蚊锹,其實(shí)已經(jīng)可以總結(jié)出EventBus中訂閱者注冊的核心邏輯了,就是篩選出注冊的類中帶有Subscribe注解的方法稚瘾,然后將其解析為SubscriberMethod對象牡昆,以訂閱的事件類型為key,SubscriberMethod對象為value的形式存入subscribedEvents中摊欠。
對于粘性事件的發(fā)送丢烘,其實(shí)與之后的一般事件執(zhí)行邏輯相同,這里就不再深入了些椒。
EventBus.getDefault().post(new MessageEvent("Hello everyone!"))
EventBus.post(Object event)
public void post(Object event) {
//從ThreadLocal中取出PostingThreadState對象播瞳,PostingThreadState對象只是保存一些消息發(fā)送過程中需要的信息
//使用ThreadLocal保證PostingThreadState對象在各線程間相互獨(dú)立,以此實(shí)現(xiàn)線程安全
PostingThreadState postingState = currentPostingThreadState.get();
//從PostingThreadState對象中取出當(dāng)前線程的消息隊(duì)列免糕,并將需要發(fā)送的消息加入隊(duì)列中
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
//開始遍歷執(zhí)行消息隊(duì)列
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
//遍歷結(jié)束赢乓,重置PostingThreadState對象
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
在這段代碼中忧侧,isMainThread()方法可以用來判斷消息發(fā)送是否是在主線程中。其中的邏輯非常簡單牌芋,通過Looper類中的getMainLooper()獲取到主線程中的Looper對象蚓炬,然后再通過Looper類中的myLooper()方法獲取當(dāng)前線程的Looper對象,然后相互比較躺屁。
EventBus.postSingleEvent(Object event, PostingThreadState postingState)
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//如果該消息支持事件繼承肯夏,則獲取該消息類型的所有父類與接口類型,并分別調(diào)用postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass)方法
if (eventInheritance) {
//獲取消息類型的所有父類與接口
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
//對于ArrayList而言犀暑,使用基本的for循環(huán)實(shí)現(xiàn)遍歷效率更高
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
//如果該消息沒有訂閱者驯击,則根據(jù)配置輸出相應(yīng)的log或發(fā)送消息
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(Object event, PostingThreadState postingState, Class<?> eventClass)
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//從Map中將該消息的訂閱事件列表取出
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
//將消息發(fā)送給訂閱者
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;
}
這個(gè)方法的主要作用就是獲得消息的訂閱事件列表,完成對于postingState中參數(shù)的賦值耐亏,其中對于所有的訂閱事件徊都,使用的其實(shí)是同一個(gè)PostingThreadState對象,只是對于不同的訂閱事件广辰,會(huì)改變其中的部分參數(shù)的值碟贾。
EventBus.postToSubscription(Subscription subscription, Object event, boolean isMainThread)
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);
}
}
在默認(rèn)情況下,EventBus會(huì)在調(diào)用線程中直接執(zhí)行invokeSubscriber方法調(diào)用訂閱事件轨域,但當(dāng)調(diào)用線程與期望的執(zhí)行線程不一致或希望異步調(diào)用時(shí),使用Poster來進(jìn)行不同線程間的調(diào)度杀餐,每一個(gè)Poster中都會(huì)持有一個(gè)消息隊(duì)列干发,并在指定線程執(zhí)行。
EventBus.invokeSubscriber(Subscription subscription, Object event)
void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
//如果捕獲到InvocationTargetException異常史翘,則根據(jù)配置打印Log或發(fā)送異常消息
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
這段代碼也就是訂閱事件執(zhí)行部分的最后一段代碼了枉长,可以看到邏輯非常的簡單,就是通過反射來執(zhí)行相應(yīng)的訂閱事件琼讽。
總結(jié)
在上文的分析中可以看出必峰,EventBus的主要邏輯非常簡單,核心流程就是注冊時(shí)先解析出帶有相應(yīng)注解的方法钻蹬,然后將其的Method對象與其所訂閱的消息類型綁定加入到一個(gè)集合中吼蚁,然后發(fā)送消息時(shí)獲取到消息類型所對應(yīng)的訂閱事件的Method對象,通過反射來調(diào)用執(zhí)行问欠。