以下是基于3.0的代碼進(jìn)行的
git倉(cāng)庫(kù):https://github.com/greenrobot/EventBus.git
簡(jiǎn)介
簡(jiǎn)單來(lái)說(shuō),EventBus是用在Activity容劳,Service陈哑,F(xiàn)ragment以及Thread之間的一個(gè)事件總線框架恬叹,用來(lái)在他們之間傳遞消息识樱。
使用
下面簡(jiǎn)述一下用法迂烁。
- module級(jí)別的gradle中添加依賴
compile'org.greenrobot:eventbus:3.0.0'
- 拿Activity為例恭陡,在onCreate中添加注冊(cè)代碼
EventBus.getDefault().register(this)
勋功,同時(shí)在onDestory中添加注銷代碼EventBus.getDefault().unregister(this)
3.在當(dāng)前事件的訂閱者中添加Event方法坦报,方法內(nèi)部參數(shù)為你的事件類型。
4.在事件發(fā)送的class里面通過(guò)post(事件類型)來(lái)傳達(dá)你的信息
解析
還是帶著問(wèn)題來(lái)看代碼吧狂鞋,這樣到最后至少給自己一個(gè)交代:理解了什么片择。
其實(shí)要去看源碼的時(shí)候,大都是會(huì)用這樣一個(gè)東西了骚揍,那我就會(huì)想字管,如果要我自己去實(shí)現(xiàn)這個(gè)bus我會(huì)怎么做啰挪。嗯,
a. 得有個(gè)東西收集我的訂閱的事情吧嘲叔,然后當(dāng)有人發(fā)布事件的時(shí)候亡呵,還能通知我;
b. 也得有個(gè)方法硫戈,當(dāng)我被銷毀的時(shí)候锰什,事件是不是也得被移除,不刪除的話必然npe了丁逝;
c. 粘性事件
d. 事件分發(fā)的原理
注冊(cè)過(guò)程
public void register(Object subscriber) {
//獲取訂閱者的類名
Class<?> subscriberClass = subscriber.getClass();
// 根據(jù)類名去查找他訂閱的方法 SubscriberMethod里面包含訂閱者的類名汁胆,方法的優(yōu)先級(jí),線程以及方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//遍歷當(dāng)前類的所有訂閱方法霜幼,把他們放進(jìn)list里面
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
// 下面是訂閱方法
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
//將subscriber和subscriberMethod封裝成 Subscription
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//根據(jù)事件類型獲取特定的 Subscription
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//如果為null嫩码,說(shuō)明該subscriber尚未注冊(cè)該事件
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
//如果不為null,并且包含了這個(gè)subscription 那么說(shuō)明該subscriber已經(jīng)注冊(cè)了該事件罪既,拋出異常
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
//根據(jù)優(yōu)先級(jí)來(lái)設(shè)置放進(jìn)subscriptions的位置铸题,優(yōu)先級(jí)高的會(huì)先被通知
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;
}
}
//根據(jù)subscriber(訂閱者)來(lái)獲取它的所有訂閱事件
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
//把訂閱者、事件放進(jìn)typesBySubscriber這個(gè)Map中
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//下面是對(duì)粘性事件的處理
if (subscriberMethod.sticky) {
//從EventBusBuilder可知琢感,eventInheritance默認(rèn)為true.
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 {
//根據(jù)eventType回挽,從stickyEvents列表中獲取特定的事件
Object stickyEvent = stickyEvents.get(eventType);
//分發(fā)事件
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
首先,他先去自己家里面找猩谊,看一下這個(gè)對(duì)象,準(zhǔn)確的說(shuō)應(yīng)該是這個(gè)類祭刚,有沒(méi)有在EventBus里面注冊(cè)過(guò)如果注冊(cè)過(guò)就直接拿出保存method的list牌捷,否則就建立個(gè)list,通過(guò)注解信息(findUsingReflection)涡驮,或者(findUsingInfo)兩個(gè)方法來(lái)拿到注冊(cè)的方法暗甥。然后以eventType(類名)作為key,將他們存起來(lái)捉捅。
粘性事件(stickyEvent)
粘性事件與一般的事件不同撤防,粘性事件是先發(fā)送出去,然后讓后面注冊(cè)的訂閱者能夠收到該事件.
事件發(fā)送的時(shí)候是通過(guò)postSticky
方法發(fā)送的棒口,然后把事件放到stickyEvents這個(gè)map里面去寄月,和原來(lái)的區(qū)分開(kāi),最后調(diào)用post方法无牵。
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
那么為什么當(dāng)注冊(cè)訂閱者的時(shí)候可以馬上接收到匹配的事件呢漾肮。這是由于在subscribe方法中,我們都會(huì)便利一遍所有的粘性事件茎毁,然后調(diào)用checkPostStickyEventToSubscription
方法進(jìn)行分發(fā)克懊。
unRegister 注銷過(guò)程
public synchronized void unregister(Object subscriber) {
//根據(jù)當(dāng)前訂閱者來(lái)獲取自身所訂閱的所有事件
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
//遍歷所有事件注銷
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
// 注銷每個(gè)事件之后忱辅,訂閱者也要被清除掉
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
結(jié)合注冊(cè)過(guò)程,注銷比較容易理解谭溉。我注冊(cè)的時(shí)候不是有兩個(gè)集合放訂閱方法和訂閱者嗎墙懂,注銷的時(shí)候我就從typesBySubscriber里面拿到當(dāng)前subscriber訂閱的所有方法,讓subscriptionsByEventType去刪除扮念,然后再把這個(gè)訂閱者拿掉损搬。
事件如何調(diào)用
/** Posts the given event to the event bus. */
public void post(Object event) {
//獲取一個(gè)postingState
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
//將事件加入隊(duì)列中
eventQueue.add(event);
if (!postingState.isPosting) {
//判斷當(dāng)前線程是否是主線程
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
//循環(huán)從隊(duì)列中將事件進(jìn)行分發(fā)
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//eventInheritance 事件繼承。EventBus會(huì)考慮事件的繼承樹(shù)
//如果事件繼承自父類扔亥,那么父類也會(huì)作為事件被發(fā)送
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);
}
//如果沒(méi)人訂閱這個(gè)事件就拋出異常
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
//經(jīng)過(guò)一系列操作之后 來(lái)到最終的分發(fā)方法
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);
}
}
//通過(guò)反射來(lái)調(diào)用事件
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);
}
}
事件發(fā)送代碼比較清晰场躯,就不細(xì)講了。
總結(jié)
EventBus的工作原理旅挤,主要分四個(gè)方面踢关,注冊(cè)注銷,事件分發(fā)與消化粘茄。
EventBus里面有兩個(gè)map签舞,一個(gè)subscriptionsByEventType的key是訂閱方法(eventType:onEvent(EventType e)),value是包含key下面的訂閱方法對(duì)象(Subscription)柒瓣;
另一個(gè)map(typesBySubscriber)儒搭,key是訂閱者類名(subscriber),value是訂閱的事件類型(eventType)芙贫。另一個(gè)map(typesBySubscriber)以訂閱者類名為key,訂閱者的eventTypes為value搂鲫,剛好能串起兩兄弟。
- 注冊(cè) 注冊(cè)的時(shí)候就抽出Subscriber里面的訂閱方法磺平,根據(jù)方法的優(yōu)先級(jí)魂仍,是否是粘性事件等信息,包裝成一個(gè)subscription拣挪,分別存到上述兩個(gè)容器中擦酌。
-
注銷 注冊(cè)注銷傳進(jìn)來(lái)的都是類對(duì)象,注銷的時(shí)候根據(jù)類對(duì)象的className菠劝,去typesBySubscriber里面找對(duì)應(yīng)的eventTypes(list)赊舶,然后遍歷list的value,以value為key,去subscriptionsByEventType
中迭代拿掉注銷的內(nèi)容。 - 事件發(fā)送 post的時(shí)候赶诊,先去找eventType的父類以及接口笼平,然后把所有的事件發(fā)送出去,牽扯到線程切換一類的都由封裝的一個(gè)poster處理舔痪。
- 事件接收 接收就很簡(jiǎn)單了出吹,subscriber拿得到,方法也拿得到辙喂,直接去調(diào)用就好了捶牢。
為什么會(huì)有兩個(gè)呢鸠珠,內(nèi)容好像都差不多:你想啊,你發(fā)送事件的時(shí)候秋麸,發(fā)的是事件(eventType)吧渐排,如果只有一個(gè)容器,你是不是還得遍歷類名灸蟆,然后遍歷類中的方法去調(diào)用驯耻。兩個(gè)容器剛好根據(jù)功能區(qū)分開(kāi),一個(gè)用在注冊(cè)的時(shí)候炒考,另一個(gè)用在查狀態(tài)以及注銷的時(shí)候可缚,你注銷不是傳的類名嗎,我就去typesBySubscriber里面拿到你的信息斋枢,然后根據(jù)subscriptionsByEventType帘靡,去里面剔除掉你,效率也提高了瓤帚,也容易理解了描姚。
結(jié)語(yǔ)
以上是對(duì)EventbUs3.0的簡(jiǎn)單解析,歡迎勘誤