EventBus源碼分析+面試總結

概述

EventBus可以代替Android傳統的Intent、Handler、Broadcast或接口函數,在Fragment、Activity、Service線程之間傳遞數據。具體使用方式參考github,本文我們將通過源碼看下他是如何註冊和發佈事件的。

GitHub地址:https://github.com/greenrobot/EventBus

1.獲取實例

//是一個“雙重校驗鎖”的單例模式。
  public static EventBus getDefault() {
        if (defaultInstance == null) {
            Class var0 = EventBus.class;
            synchronized(EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }

        return defaultInstance;
    }
    public EventBus() {
        this(DEFAULT_BUILDER);
    }

    EventBus(EventBusBuilder builder) {
        this.currentPostingThreadState = new ThreadLocal<EventBus.PostingThreadState>() {
            protected EventBus.PostingThreadState initialValue() {
                return new EventBus.PostingThreadState();
            }
        };
        this.logger = builder.getLogger();
        this.subscriptionsByEventType = new HashMap();
        this.typesBySubscriber = new HashMap();
        this.stickyEvents = new ConcurrentHashMap();
        this.mainThreadSupport = builder.getMainThreadSupport();
        this.mainThreadPoster = this.mainThreadSupport != null ? this.mainThreadSupport.createPoster(this) : null;
        this.backgroundPoster = new BackgroundPoster(this);
        this.asyncPoster = new AsyncPoster(this);
        this.indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        this.subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes, builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        this.logSubscriberExceptions = builder.logSubscriberExceptions;
        this.logNoSubscriberMessages = builder.logNoSubscriberMessages;
        this.sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        this.sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        this.throwSubscriberException = builder.throwSubscriberException;
        this.eventInheritance = builder.eventInheritance;
        this.executorService = builder.executorService;
    }

2.註冊事件

調用註冊方法:

EventBus.getDefault().register(this);

當調用EventBus實例的register方法的時候,會執行下面的邏輯:

    public void register(Object subscriber) {
     	//獲取註冊的對象的類型
        Class<?> subscriberClass = subscriber.getClass();
        // 然後獲取註冊的對象的訂閱方法
        List<SubscriberMethod> subscriberMethods = this.subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized(this) {
            Iterator var5 = subscriberMethods.iterator();
			// 對每個訂閱方法進行註冊
            while(var5.hasNext()) {
                SubscriberMethod subscriberMethod = (SubscriberMethod)var5.next();
                this.subscribe(subscriber, subscriberMethod);
            }
        }
    }

這裏的SubscriberMethod封裝了訂閱方法(使用@Subscribe註解的方法)類型的信息,它的定義如下所示。從下面可以的代碼中我們可以看出,實際上該類就是通過幾個字段來存儲@Subscribe註解中指定的類型信息,以及一個方法的類型變量。

public class SubscriberMethod {
    final Method method;//註冊方法
    final ThreadMode threadMode;//線程模式
    final Class<?> eventType;//事件類型
    final int priority;//事件優先級
    final boolean sticky;//是否粘性事件
    String methodString;
    ...
}

register方法通過subscriberMethodFinder實例的findSubscriberMethods方法來獲取該觀察者類型中的所有訂閱方法,然後將所有的訂閱方法分別進行訂閱。下面我們先看下查找訂閱者的方法。

    private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap();

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    // 這裏首先從緩存當中嘗試去取該訂閱者的訂閱方法
        List<SubscriberMethod> subscriberMethods = (List)METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        } else {
         // 當緩存中沒有找到該觀察者的訂閱方法的時候使用下面的兩種方法獲取方法信息
            if (this.ignoreGeneratedIndex) {
                subscriberMethods = this.findUsingReflection(subscriberClass);
            } else {
                subscriberMethods = this.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.put(subscriberClass, subscriberMethods);
                return subscriberMethods;
            }
        }
    }
 

這裏的ignoreGeneratedIndex參數表示是否忽略註解器生成的MyEventBusIndex,該值默認爲false。然後,我們會進入到下面的方法中獲取訂閱方法信息:

   private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
   // 這裏通過FindState對象來存儲找到的方法信息
        SubscriberMethodFinder.FindState findState = this.prepareFindState();
        findState.initForSubscriber(subscriberClass);
		//從當前類開始,遍歷該類的所有父類
        for(; findState.clazz != null; findState.moveToSuperclass()) {
        // 這裏通過FindState對象來存儲找到的方法信息
            findState.subscriberInfo = this.getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) {
            // 如果使用了MyEventBusIndex,將會進入到這裏並獲取訂閱方法信息
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                SubscriberMethod[] var4 = array;
                int var5 = array.length;

                for(int var6 = 0; var6 < var5; ++var6) {
                    SubscriberMethod subscriberMethod = var4[var6];
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                // 未使用MyEventBusIndex將會進入這裏使用反射獲取方法信息
                this.findUsingReflectionInSingleClass(findState);
            }
        }

        return this.getMethodsAndRelease(findState);
    }
   

findUsingReflectionInSingleClass方法從訂閱者的所有方法中找到訂閱方法

private void findUsingReflectionInSingleClass(FindState findState) {
 Method[] methods;
 try {
     // 獲取該類中聲明的所有方法
     methods = findState.clazz.getDeclaredMethods();
 } catch (Throwable th) {
     methods = findState.clazz.getMethods();
     findState.skipSuperClasses = true;
 }
 // 對方法進行遍歷判斷
 for (Method method : methods) {
     int modifiers = method.getModifiers();
     // 這裏會對方法的修飾符進行校驗
     if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
         // 這裏對方法的輸入參數進行校驗,選擇只有1個參數
         Class<?>[] parameterTypes = method.getParameterTypes();
         if (parameterTypes.length == 1) {
             // 獲取方法的註解,用來從註解中獲取註解的聲明信息
             Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
             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(...);
         }
     } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
         String methodName = method.getDeclaringClass().getName() + "." + method.getName();
         throw new EventBusException(...);
     }
 }
}

以上是如何獲取到訂閱類中的所有訂閱方法,我們再回到開始的register中,看下訂閱的過程:

    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        // 將觀察者和訂閱方法封裝成一個Subscription對象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        // 嘗試從緩存中根據事件類型來獲取所有的Subscription對象
        CopyOnWriteArrayList<Subscription> subscriptions = (CopyOnWriteArrayList)this.subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
        // 指定的事件類型沒有對應的觀察對象的時候
            subscriptions = new CopyOnWriteArrayList();
            this.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 > ((Subscription)subscriptions.get(i)).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }
    	// 這裏又會從“訂閱者-事件類型”列表中嘗試獲取該訂閱者對應的所有事件類型
        List<Class<?>> subscribedEvents = (List)this.typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList();
            this.typesBySubscriber.put(subscriber, subscribedEvents);
        }

        ((List)subscribedEvents).add(eventType);
        // 如果是黏性事件還要進行如下的處理
        if (subscriberMethod.sticky) {
            if (this.eventInheritance) {
                Set<Entry<Class<?>, Object>> entries = this.stickyEvents.entrySet();
                Iterator var9 = entries.iterator();

                while(var9.hasNext()) {
                    Entry<Class<?>, Object> entry = (Entry)var9.next();
                    Class<?> candidateEventType = (Class)entry.getKey();
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
						// 這裏會向該觀察者通知所有的黏性事件
                        this.checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = this.stickyEvents.get(eventType);
                this.checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

訂閱者(比如MainActivity.this)與事件消費方法(SubscriberMethod)的關係,我們封裝到了Subscription中了。而Event和Subscription的關係,我們通過HashMap保存,key爲event.class,value即Subscription。

這裏涉及到了幾個集合,它們是用來做緩存的:

subscriptionsByEventType是根據參數類型存儲訂閱者和訂閱方法,主要是post時使用,因爲其存儲了訂閱者和訂閱方法,這兩個參數在反射時要用到。

typesBySubscriber根據訂閱者存儲所有的參數類型,在反註冊時可以使用。

有了註冊那麼肯定還有反註冊:

    public synchronized void unregister(Object subscriber) {
        List<Class<?>> subscribedTypes = (List)this.typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            Iterator var3 = subscribedTypes.iterator();

            while(var3.hasNext()) {
                Class<?> eventType = (Class)var3.next();
                this.unsubscribeByEventType(subscriber, eventType);
            }

            this.typesBySubscriber.remove(subscriber);
        } else {
            this.logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }
    
   private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        List<Subscription> subscriptions = (List)this.subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {
            int size = subscriptions.size();

            for(int i = 0; i < size; ++i) {
                Subscription subscription = (Subscription)subscriptions.get(i);
                if (subscription.subscriber == subscriber) {//遍歷找到該訂閱者的訂閱事件
                    subscription.active = false;
                    subscriptions.remove(i);//移除
                    --i;
                    --size;
                }
            }
        }

    }


3.發送事件

    private final ThreadLocal<EventBus.PostingThreadState> currentPostingThreadState;

    public void post(Object event) {
        // 這裏從線程局部變量中取出當前線程的狀態信息
        EventBus.PostingThreadState postingState = (EventBus.PostingThreadState)this.currentPostingThreadState.get();
        // 這裏是以上線程局部變量內部維護的一個事件隊列
        List<Object> eventQueue = postingState.eventQueue;
        // 將當前要發送的事件加入到隊列中
        eventQueue.add(event);
        if (!postingState.isPosting) {
            postingState.isMainThread = this.isMainThread();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }

            try {
            //循環發送事件
                while(!eventQueue.isEmpty()) {
                    this.postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                // 恢復當前線程的信息
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }

    }

這裏的currentPostingThreadState是一個ThreadLocal類型的變量,其中存儲了對應於當前線程的PostingThreadState對象,該對象中存儲了當前線程對應的事件列表和線程的狀態信息等。從上面的代碼中可以看出,post方法會不斷從當前線程對應的隊列中取出事件並進行發佈。下面我們看以下這裏的postSingleEvent方法。

    private void postSingleEvent(Object event, EventBus.PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        if (this.eventInheritance) {
            // 這裏向上查找該事件的所有父類
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();

            for(int h = 0; h < countTypes; ++h) {
                Class<?> clazz = (Class)eventTypes.get(h);
                // 對上面的事件進行處理
                subscriptionFound |= this.postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            subscriptionFound = this.postSingleEventForEventType(event, postingState, eventClass);
        }

        if (!subscriptionFound) {
            if (this.logNoSubscriberMessages) {
                this.logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }

            if (this.sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) {
                this.post(new NoSubscriberEvent(this, event));
            }
        }

    }

在上面的代碼中,我們會根據eventInheritance的值決定是否要同時遍歷當前事件的所有父類的事件信息並進行分發。如果設置爲true就將執行這一操作,並最終使用postSingleEventForEventType對每個事件類型進行處理。

    private boolean postSingleEventForEventType(Object event, EventBus.PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList subscriptions;
        synchronized(this) {
            // 獲取指定的事件對應的所有的觀察對象
            subscriptions = (CopyOnWriteArrayList)this.subscriptionsByEventType.get(eventClass);
        }

        if (subscriptions != null && !subscriptions.isEmpty()) {
            Iterator var5 = subscriptions.iterator();
        // 遍歷觀察對象,並最終執行事件的分發操作
            while(var5.hasNext()) {
                Subscription subscription = (Subscription)var5.next();
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted = false;

                try {
                    this.postToSubscription(subscription, event, postingState.isMainThread);
                    aborted = postingState.canceled;
                } finally {
                    postingState.event = null;
                    postingState.subscription = null;
                    postingState.canceled = false;
                }

                if (aborted) {
                    break;
                }
            }

            return true;
        } else {
            return false;
        }
    }

在上面的代碼中,我們會通過傳入的事件類型到緩存中取尋找它對應的全部的Subscription,然後對得到的Subscription列表進行遍歷,並依次調用postToSubscription方法執行事件的發佈操作。下面是postToSubscription方法的代碼,這裏我們會根據訂閱方法指定的threadMode信息來執行不同的發佈策略。

ThreadMode:

  • POSTING: 默認的模式,開銷最小的模式,因爲聲明爲POSTING的訂閱者會在發佈的同一個線程調用,發佈者在主線程那麼訂閱者也就在主線程,反之亦,避免了線程切換,如果不確定是否有耗時操作,謹慎使用,因爲可能是在主線程發佈
  • MAIN:主線程調用,視發佈線程不同處理不同,如果發佈者在主線程那麼直接調用(非阻塞式),如果發佈者不在主線程那麼阻塞式調用
  • MAIN_ORDERED:和MAIN差不多,主線程調用,和MAIN不同的是他保證了post是非阻塞式的(默認走MAIN的非主線程的邏輯,所以可以做到非阻塞)
  • BACKGROUND:在子線程調用,如果發佈在子線程那麼直接在發佈線程調用,如果發佈在主線程那麼將開啓一個子線程來調用,這個子線程是阻塞式的,按順序交付所有事件,所以也不適合做耗時任務,因爲多個事件共用這一個後臺線程
  • ASYNC:在子線程調用,總是開啓一個新的線程來調用,適用於做耗時任務,比如數據庫操作,網絡請求等,不適合做計算任務,會導致開啓大量線程
    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch(subscription.subscriberMethod.threadMode) {
        case POSTING:
            this.invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {
                this.invokeSubscriber(subscription, event);
            } else {
                this.mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case MAIN_ORDERED:
            if (this.mainThreadPoster != null) {
                this.mainThreadPoster.enqueue(subscription, event);
            } else {
                this.invokeSubscriber(subscription, event);
            }
            break;
        case BACKGROUND:
            if (isMainThread) {
                this.backgroundPoster.enqueue(subscription, event);
            } else {
                this.invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
            this.asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }

    }

這裏的invokeSubscriber會在當前線程中立即調用反射來觸發指定的觀察者的訂閱方法。否則會根據具體的情況將事件加入到不同的隊列中進行處理。

這裏的mainThreadPoster最終繼承自Handler,當調用它的enqueue方法的時候,它會發送一個事件並在它自身的handleMessage方法中從隊列中取值並進行處理,從而達到在主線程中分發事件的目的。

這裏的backgroundPoster實現了Runnable接口,它會在調用enqueue方法的時候,拿到EventBus的ExecutorService實例,並使用它來執行自己。在它的run方法中會從隊列中不斷取值來進行執行。

問題

1、 在EventBus中,使用@Subscribe註解的時候指定的ThreadMode是如何實現在不同線程間傳遞數據的?

要求主線程中的事件通過Handler來實現在主線程中執行,非主線程的方法會使用EventBus內部的ExecutorService來執行。實際在觸發方法的時候會根據當前線程的狀態和訂閱方法的ThreadMode指定的線程狀態來決定何時觸發方法。非主線程的邏輯會在post的時候加入到一個隊列中被隨後執行。

2、黏性事件是否是通過內部維護了之前發佈的數據來實現的,是否使用了緩存?

黏性事件會通過EventBus內部維護的一個事件類型-黏性事件的哈希表存儲,當註冊一個觀察者的時候,如果發現了它內部有黏性事件監聽,會執行post類似的邏輯將事件立即發送給該觀察者。


總結

1. 註冊事件

在這裏插入圖片描述

涉及參數

Subscription newSubscription = new Subscription(subscriber, subscriberMethod); // Subscription類保存了要註冊的類對象以及當前的subscriberMethod
Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; //保存了以eventType爲key,Subscription對象集合爲value的鍵值對,根據參數類存儲訂閱者和訂閱方法
Map<Object, List<Class<?>>> typesBySubscriber; //保存了以當前要註冊類的對象爲key,註冊類中訂閱事件的方法的參數類型的集合爲value的鍵值對,根據訂閱者存儲所有參數類型

總結:主要是register方法,獲取註冊類中訂閱了事件的所有方法(即使用了Subscribe註解、有public修飾符、一個參數的方法),然後看緩存中(subscriptionsByEventType和typesBySubscriber)有沒有,沒有就保存進subscriptionsByEventType和typesBySubscriber(兩個 HashMap,subscriptionsByEventType在發送事件時使用,typesBySubscriber在取消註冊時使用)

2.發送事件(含線程切換)

在這裏插入圖片描述

總結:調用post方法,獲取事件的類型,根據類型從subscriptionsByEventType中獲取訂閱者和訂閱方法,然後根據 發送事件的線程 和 事件訂閱的線程模式 判斷是否需要切換線程(子線程往主線程切換用handler、主線程往子線程切換用線程池),然後通過反射調用訂閱者的訂閱方法,完成通訊

3.粘性事件

總結

  1. 調用postSticky方法,postSticky()方法主要做了兩件事:先將事件類型和對應事件保存到stickyEvents中,方便後續使用;然後執行post(event)繼續發送事件,這個post()方法就是之前發送的post()方法(所以,如果在發送粘性事件前,已經有了對應類型事件的訂閱者,及時它是非粘性的,依然可以接收到發送出的粘性事件)。
  2. 在註冊訂閱事件的時候,遍歷stickyEvents,如果當前要註冊的事件訂閱方法是粘性的,並且該方法接收的事件類型和stickyEvents中某個事件類型相同或者是其父類,則響應訂閱事件
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章