最近閱讀了EventBus(3.0.0)的源碼,這裏也是記錄下本身對EventBus的理解,功力善淺,若有錯誤地方還望各位大佬及時指正。緩存
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
複製代碼
關聯Activity或者Fragment的生命週期,在onStart方法中註冊,在onStop方法中反註冊,防止內存泄漏安全
public class MessageEvent {
public String message;
public MessageEvent(String message) {
this.message = message;
}
}
複製代碼
一個簡單的事件實體類,將發送消息的內容存儲於這個類的實例中bash
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Log.d(TAG,"收到了一條EventBus消息" + event.message);
}
複製代碼
這裏有一個Subscribe註解,括號裏的threadMode = ThreadMode.MAIN指定了方法執行的線程,3.0之後方法名onMessageEvent支持修改。收到了MessageEvent 的消息便會在主線程執行此方法。同時threadMode目前有四種線程模式:併發
EventBus.getDefault().post(new MessageEvent("我是一條EventBus消息"));
複製代碼
發送消息後咱們接收的消息方法便會執行。app
咱們能夠發現不管是咱們註冊和反註冊仍是發送消息,咱們都是先調用了EventBus的getDefault方法。這裏咱們先看一下這個方法異步
static volatile EventBus defaultInstance;
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
複製代碼
很明顯這裏是得到一個EventBus類的單例。咱們再來看一下EventBus的構造方法async
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
/**
* Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
* central bus, consider {@link #getDefault()}.
*/
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
//存放訂閱事件對應的Subscription(封裝訂閱者對象對應的訂閱事件封裝容器類SubscriberMethod的容器類)對象集合
subscriptionsByEventType = new HashMap<>();
//存放訂閱者對象對應訂閱事件的集合
typesBySubscriber = new HashMap<>();
//粘性事件訂閱事件對應的訂閱者對象集合
stickyEvents = new ConcurrentHashMap<>();
//主線程的handler對象,主要是用來執行在訂閱在主線程執行的事件
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
//後臺線程的Runnable,主要是經過線程池成員變量executorService來執行訂閱在後臺線程執行的事件
backgroundPoster = new BackgroundPoster(this);
//異步線程的Runnable,主要是經過線程池成員變量executorService來執行訂閱在非當前線程執行的事件
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;
}
複製代碼
能夠看出無參構造函數調用了有參構造函數,參數是一個EventBusBuilder 實例。有參構造裏面也是對EventBus的成員變量進行賦值,部分也是直接賦值於EventBusBuilder 的成員變量,其實看到Builder咱們就很容易猜到這裏也是採用了構建者模式,除了經過getDefault方法得到EventBus實例,咱們也能夠經過EventBusBuilder來設置成員變量來構建EventBus實例。這裏就不細說了。ide
接下來咱們再來看一下注冊的實現。函數
/**
* Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
* are no longer interested in receiving events.
* <p/>
* Subscribers have event handling methods that must be annotated by {@link Subscribe}.
* The {@link Subscribe} annotation also allows configuration like {@link
* ThreadMode} and priority.
*/
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
//經過subscriberMethodFinder找到訂閱對象中的全部訂閱事件方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//遍歷訂閱對象中全部的訂閱事件,將其緩存到EventBus的subscriptionsByEventType集合中
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
複製代碼
這裏先是拿到了註冊對象的class對象,而後調用了SubscriberMethodFinder類的findSubscriberMethods方法oop
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//先從METHOD_CACHE集合中查找訂閱者對應的訂閱事件集合
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//是否忽略註解器生成的MyEventBusIndex類,默認爲false
if (ignoreGeneratedIndex) {
//經過反射查找訂閱者類中對應的訂閱事件集合
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//經過註解器生成的MyEventBusIndex類查找訂閱者類中對應的訂閱事件集合
subscriberMethods = 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集合中
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
複製代碼
METHOD_CACHE 是一個ConcurrentHashMap,咱們以前在Retorfit中看到過這個,這是一個線程安全的map集合。SubscriberMethodFinder類的ignoreGeneratedIndex是一個布爾類型的,EventBus在建立SubscriberMethodFinder實例時給這個變量賦值是直接採用了EventBusBuilder的ignoreGeneratedIndex的值,默認狀況下是false。接着會繼續findUsingInfo方法,
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//經過MyEventBusIndex類獲取訂閱者類中對應的訂閱事件集合
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);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
複製代碼
這裏FindState實例至關於一個臨時的存儲器,findUsingReflectionInSingleClass方法來檢查註冊類裏面是否帶有 @Subscribe註解的方法,並檢測其合法性(是不是共public方法,是否非靜態,是否只有一個參數等等),而後用@Subscribe註解的方法名,方法參數,執行線程,優先級這些信息構建出一個SubscriberMethod類實例,存儲在FindState實例中,最後經過getMethodsAndRelease方法或者存儲在FindState實例中的SubscriberMethod的list,而後釋放FindState實例的引用,可是不置空FindState實例,以便下次直接使用。最終findSubscriberMethods方法就是返回了註冊類中合法的@Subscribe註解方法集合,找到了便將其緩存起來並返回,若是找不到,便會拋出異常。最後看下register方法中遍歷了註冊類中@Subscribe註解方法構成的的SubscriberMethod的list,調用了subscribe方法。
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//拿到訂閱事件類型的Class對象
Class<?> eventType = subscriberMethod.eventType;
//Subscription是一個封裝訂閱對象和SubscriberMethod對象的封裝類
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//在EventBus的subscriptionsByEventType集合中緩存訂閱事件類型的Class對象對應的Subscription集合,檢查訂閱類是否已經註冊過了
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
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);
}
}
//根據優先級順序在對應的Subscription集合中來插入本次建立的Subscription實例
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;
}
}
//在EventBus的typesBySubscriber(Map集合)中緩存訂閱類中對應的訂閱事件集合
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//處理粘性事件
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);
}
}
}
複製代碼
總結一下這個方法主要將訂閱類中的訂閱事件緩存到EventBus的subscriptionsByEventType和typesBySubscriber中,以便後續發送事件使用
/** Posts the given event to the event bus. */
public void post(Object event) {
//取出當前線程存儲的PostingThreadState實例,當中存儲了當前線程的事件相關信息。
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
//將事件添加到事件隊列中
eventQueue.add(event);
//判斷線程是否是有事件正在發送
if (!postingState.isPosting) {
//判斷當前線程是否是主線程
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
//循環事件隊列,從中取出消息發送
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
複製代碼
post方法主要是循環當前線程中保存的PostingThreadState實例中的事件隊列,取出並經過postSingleEvent方法對事件進行進一步處理。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//是否對事件的父類也進行發送,EventBusBuilder中默認爲true
if (eventInheritance) {
//尋找其繼承關係,將其父類,以及父類的父類等也添加到eventTypesCache集合中,
// // 返回的eventTypes集合就包含了eventClass以及eventClass的父類,父類的父類,eventClass實現的接口以及接口實現的接口等
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
//遍歷eventTypes中的事件,進行發送
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
//直接對事件進行發送
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
//若是沒有事件訂閱者則打印日誌,併發送一個NoSubscriberEvent事件
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方法主要是判斷是否連事件的的父類事件也要一塊兒發送,以及對發送事件沒有訂閱者的狀況進行處理。這裏調用了postSingleEventForEventType方法來進一步處理
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//從subscriptionsByEventType集合中獲取Subscription對象集合
subscriptions = subscriptionsByEventType.get(eventClass);
}
//遍歷訂閱者集合
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 {
//還原當前線程PostingThreadState實例中的成員變量
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
//若是取消發送則跳出循環返回false
if (aborted) {
break;
}
}
return true;
}
//沒有訂閱者返回false
return false;
}
複製代碼
postSingleEventForEventType方法主要是經過EventBus中的subscriptionsByEventType集合找出事件對應的Subscription對象,而後經過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 {
//經過主線程的handler來發送事件
mainThreadPoster.enqueue(subscription, event);
}
break;
case BACKGROUND://訂閱事件在後臺線程執行
if (isMainThread) {
//當前線程是主線程則最終經過EventBus中的executorService線程池執行事件
backgroundPoster.enqueue(subscription, event);
} else {
//當前線程是後臺線程則直接執行事件
invokeSubscriber(subscription, event);
}
break;
case ASYNC://訂閱事件在別的線程異步執行
//直接經過EventBus中的executorService線程池執行事件
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
複製代碼
postToSubscription方法主要是根據定義事件接收時的執行的線程,用不一樣的線程經過反射來執行訂閱者對應的接收事件方法。 至此,post方法的流程也分析完了。接下來咱們再來看EventBus的unregister方法。
/** 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) {
//根據訂閱事件來找到其對應的subscriptionsByEventType中對應的Subscription對象集合
//從對應的訂閱者集合中移除此訂閱者對象
unsubscribeByEventType(subscriber, eventType);
}
//從typesBySubscriber集合中移除此訂閱對象以及其對應的訂閱事件
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
複製代碼
unregister方法主要是將訂閱者對象從EventBus緩存的訂閱者對象以及訂閱者對象訂閱的方法的相關集合中移除。 至此,EvenBus整個事件的訂閱,發送,執行,取消訂閱咱們就分析完了。大體的流程總結: