EventBus 原始碼分析
這篇文章主要是根據我們平時的使用,一步一步的分析EventBus原始碼流程,因此分為三步:
1、註冊訂閱者
2、事件釋出
3、反註冊訂閱者
1、register 註冊訂閱者
在使用eventBus的時候,第一個步驟就是註冊訂閱者
EventBus.getDefault().register(this);
getDefault方法是一個單例模式的初始化方法,主要就是獲取一個例項,原始碼如下:
public static EventBus getDefault() { EventBus instance = defaultInstance; if (instance == null) { synchronized (EventBus.class) { instance = EventBus.defaultInstance; if (instance == null) { instance = EventBus.defaultInstance = new EventBus(); } } } return instance; }
可以很清楚的看到,這裡只是一個DoubleCheck的單例模式,接下來直接看一下構造方法:
/** * 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) { 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; }
構造方法裡面是通過一個Builder模式來對EventBus各項配置進行初始化
在getDefault獲取到例項之後,就會呼叫register方法進行註冊,這時候進入register方法看一下注冊過程:
public void register(Object subscriber) { Class<?> subscriberClass = subscriber.getClass(); //獲取到訂閱者中所有的訂閱方法,並儲存到list集合中 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } }
從原始碼裡面看,註冊過程只有兩步:
a、根據註冊時傳入的訂閱者物件,找到所有的訂閱方法;
b、訂閱所有的訂閱方法
a1)、
首先看一下如何查詢到訂閱者中所有的訂閱方法,即findSubscriberMethods()方法,subscriberMethodFinder是在EventBus構造方法中進行的初始化,進入findSubscriberMethods方法中進行檢視:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { //METHOD_CACHE 一個map集合,以訂閱者為key,以訂閱方法的list集合為value進行快取 List<SubscriberMethod> subscriberMethods =METHOD_CACHE.get(subscriberClass); //如果當前的物件已經被快取,則直接獲取返回 if (subscriberMethods != null) { return subscriberMethods; } if (ignoreGeneratedIndex) { // 如果忽略索引,就根據反射來獲取 subscriberMethods = findUsingReflection(subscriberClass); } else { //否則使用索引 subscriberMethods = findUsingInfo(subscriberClass); } //如果該訂閱者中沒有訂閱方法,此處會丟擲一個異常,提醒你該訂閱者和他的父類裡面都沒有訂閱方法(public 修飾並且@Subscribe 進行註解) if (subscriberMethods.isEmpty()) { throw new EventBusException("Subscriber " + subscriberClass + " and its super classes have no public methods with the @Subscribe annotation"); } else { //如果有訂閱方法,則把該訂閱者和訂閱方法進行快取,並返回訂閱方法的list集合 METHOD_CACHE.put(subscriberClass, subscriberMethods); return subscriberMethods; } }
a1.1)、
findSubscriberMethods 裡面的邏輯已經很清晰了,現在看一下 findUsingInfo()和findUsingReflection(),一般使用的時候,並沒有自定義進行配置,所以一般都是使用索引進行查詢
a1.1.1)
這裡就先分析一下findUsingInfo()方法,反射的稍後再看:
//忽略索引時,查詢方法 private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { // 建立並初始化FindState物件 FindState是訂閱者的一個輔助類,用於獲取到訂閱方法 FindState findState = prepareFindState(); //關聯訂閱者 findState.initForSubscriber(subscriberClass); //此處通過迴圈,獲取到訂閱者父類中的訂閱方法 while (findState.clazz != null) { //獲取到訂閱者的資訊 findState.subscriberInfo = getSubscriberInfo(findState); if (findState.subscriberInfo != null) { //如果可以找到訂閱者資訊,將訂閱方法快取到 subscriberMethods 中 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類是一個輔助類,用來輔助訂閱者獲取訂閱方法,程式碼比較簡單,篇幅原因此處就不在進行原始碼解析,只要清楚這個類主要用途即可
prepareFindState() 方法則是用來建立並初始化FindState的方法,同時坐著也通過一個快取池進行了優化
從上面原始碼中可以看到,在可以直接查詢到訂閱資訊的情況比較簡單,直接將查詢到的資訊進行快取即可,下面分析一下找不到訂閱資訊的時候,通過反射方法進行查詢:
//通過反射方式,獲取到訂閱者中所有的訂閱方法 private void findUsingReflectionInSingleClass(FindState findState) { // 1 獲取訂閱者內所有方法 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; } // 2 遍歷所有方法,找到所有的訂閱方法,並將所有的訂閱方法進行快取到 findState 中 for (Method method : methods) { int modifiers = method.getModifiers(); //如果是非靜態、非抽象、public 修飾的 if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { Class<?>[] parameterTypes = method.getParameterTypes(); //只有一個引數 if (parameterTypes.length == 1) { Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); // Subscribe 進行註解 if (subscribeAnnotation != null) { Class<?> eventType = parameterTypes[0]; if (findState.checkAdd(method, eventType)) { ThreadMode threadMode = subscribeAnnotation.threadMode(); //符合條件的進行快取 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"); } } }
認真看一下每一個步驟,可以發現其實就是通過反射查詢到符合條件的訂閱方法,然後將訂閱方法快取到findState中,同時對註解的訂閱方法進行校驗,給出提示,在查詢到所有的訂閱方法之後,程式碼執行到了,a1.1.1) findUsingReflectionInSingleClass 執行完畢,然後 findUsingInfo()在迴圈查詢並快取訂閱者中所有的訂閱方法,至此, 在a1.1) findUsingInfo 方法也執行完畢,成功找到了所有的訂閱方法,a1)、中忽略索引的情況已經執行完畢
查詢完成之後,我們在回過頭看一下a1)、findSubscriberMethods 方法中不忽略 索引的方法 findUsingReflection()
FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { findUsingReflectionInSingleClass(findState); findState.moveToSuperclass(); } return getMethodsAndRelease(findState);
這段程式碼就非常簡單了,上面已經分析過了,直接使用反射查詢到所有的方法,並進行快取;
在查詢到所有的訂閱方法之後,將所有的方法進行快取到 METHOD_CACHE ,至此,註冊第一步就完成了;
無論使用哪一種方法,到這裡都已經找到所有的訂閱方法了,在再次返回到 register()方法中:
synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } }
這段程式碼通過迴圈,將所有的訂閱方法和訂閱者關聯起來,看一下subscribe 方法
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { Class<?> eventType = subscriberMethod.eventType; //建立訂閱者和訂閱方法的封裝物件 Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //由於所有的訂閱方法都只有一個引數,並且subscriptionsByEventType 是以 訂閱方法的引數的class為key,Subscription集合為value的 ////的map集合,所以,根據eventType 可以找到所有的含有此引數的訂閱方法所在的訂閱者 // 根據訂閱方法的引數型別,查詢到所有包含有該Event 的訂閱者,Event 即為訂閱方法的引數 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); } } //根據優先順序,將當前的訂閱者插入訂閱集合 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; } } //根據訂閱者,查詢訂閱者裡面所有的Event 型別,並儲存 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); } } }
至此,訂閱者和訂閱事件進行了關聯,並且進行了快取,register()完成
2、事件釋出
事件釋出一般通過以下兩種方法進行釋出,postSticky 為傳送粘性事件
EventBus.getDefault().post(new Event("我是測試event資料")); EventBus.getDefault().postSticky(new Event("我是測試event資料"));
首先先分析一下post 事件
public void post(Object event) { //獲取到ThreadLocal中儲存的資料,並將當前post的資料新增到佇列中 PostingThreadState postingState = currentPostingThreadState.get(); List<Object> eventQueue = postingState.eventQueue; eventQueue.add(event); //如果當前沒有在post 事件 if (!postingState.isPosting) { postingState.isMainThread = isMainThread(); 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; } } } 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)); } } } private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { CopyOnWriteArrayList<Subscription> subscriptions; //subscriptionsByEventType,key 為訂閱方法的引數型別,value為訂閱者和訂閱方法封裝的list集合 //根據register()的原始碼,已經知道在註冊的時候,會將訂閱者和訂閱方法快取到subscriptionsByEventType裡面 synchronized (this) { subscriptions = subscriptionsByEventType.get(eventClass); } //如果post的Event有訂閱者接收,迴圈進行訂閱 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; } // 訂閱方法的執行方法,會根據threadMode將訂閱方法在不同的執行緒進行執行 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); } }
post方法本身並不複雜,重點關注一下postToSubscription()方法,在此方法中,根據不同的執行緒進行處理,執行訂閱方法,到這裡,事件釋出就完成了,訂閱方法也已經被執行了,完成了事件的分發
3、反註冊訂閱者
反註冊就非常簡單了,僅僅是將快取的訂閱者及其訂閱方法移除即可
public synchronized void unregister(Object subscriber) { List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); if (subscribedTypes != null) { for (Class<?> eventType : subscribedTypes) { unsubscribeByEventType(subscriber, eventType); } typesBySubscriber.remove(subscriber); } else { logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass()); } } private void unsubscribeByEventType(Object subscriber, Class<?> eventType) { List<Subscription> subscriptions = subscriptionsByEventType.get(eventType); 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--; } } } }
從程式碼中可以看出,反註冊的時候將註冊時候快取在 typesBySubscriber 和subscriptionsByEventType 中的資料移除,完成了反註冊