EvenBus源碼分析

第一次在平臺寫博客,以前只是在公衆號上寫,雖然沒人看可是自娛自樂的堅持了一段時間。想一想畢竟本身也翻過資料看過別人的博客才寫出來的應該也有一些價值,無論有沒有人看也應該寫一寫爲開發社區貢獻一點看法,畢竟開源的目的不是汲取而是分享。java

正文android

一、基本使用


  • 自定義一個事件類,用於傳遞數據
public static class MessageEvent { /* Additional fields if needed */ }
複製代碼
  • 在須要訂閱事件的地方註冊事件
@Override
 public void onStart() {     
 super.onStart();    
  EventBus.getDefault().register(this);
 } 
  
 @Override
 public void onStop() { 
     super.onStop();     
     EventBus.getDefault().unregister(this);
 }
複製代碼
  • 發送事件
//普通事件,註冊後發送才能夠接收
EventBus.getDefault().post(new MessageEvent());
//黏性事件,發送後才註冊也能夠接收。訂閱者註解中須要添加sticky=true
EventBus.getDefault().postSticky(new MessageEvent());
複製代碼
  • 處理事件
**
* threadMode類型有四種:
* POSTING:默認,從什麼線程發出就在什麼線程接收
* MAIN:事件處理在主線程執行,時間太長會致使ANR
* BACKGROUNP:在主線程發出在主線程接收、在子線程發出在原線程接收
* ASYNC:不管從哪裏發出,都將在子線程接收。禁止ui操做
*
* @Subscrbe:訂閱者註解
*/
@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {
/* Do something */
};
複製代碼

二、源碼分析

  • 註冊分析

根據源碼分析後畫的圖。其實關於EventBus的源碼分析的博客有不少,這只是方便本身往後看回來更好回憶git

  • getDefault()
/** Convenience singleton for apps using a process-wide EventBus instance. */
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;
}
複製代碼

能夠發現,只是一個簡單的單例模式,那麼看看構造方法作了什麼操做。github

  • 構造方法
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;
}
複製代碼

很明顯構造方法只是進行了須要用到對象的初始化,其中EventBusBuilder是一個建造者模式的建造者。緩存

  • register(Object)
/** * 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();
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}
複製代碼

會發現兩個核心的方法
findSubscriberMethods
subscribe
還有一個核心的類
SubscribeMethodbash

  • SubscribeMethod
/** Used internally by EventBus and generated subscriber indexes. */
public class SubscriberMethod {
    final Method method;
    final ThreadMode threadMode;
    final Class<?> eventType;
    final int priority;
    final boolean sticky;
    /** Used for efficient comparison */
    String methodString;

    public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
        this.method = method;
        this.threadMode = threadMode;
        this.eventType = eventType;
        this.priority = priority;
        this.sticky = sticky;
    }

    @Override
    public boolean equals(Object other) {
        if (other == this) {
            return true;
        } else if (other instanceof SubscriberMethod) {
            checkMethodString();
            SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other;
            otherSubscriberMethod.checkMethodString();
            // Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6
            return methodString.equals(otherSubscriberMethod.methodString);
        } else {
            return false;
        }
    }

    private synchronized void checkMethodString() {
        if (methodString == null) {
            // Method.toString has more overhead, just take relevant parts of the method
            StringBuilder builder = new StringBuilder(64);
            builder.append(method.getDeclaringClass().getName());
            builder.append('#').append(method.getName());
            builder.append('(').append(eventType.getName());
            methodString = builder.toString();
        }
    }

    @Override
    public int hashCode() {
        return method.hashCode();
    }
}
複製代碼

發現SubscribeMethod就是一個封裝了訂閱者的線程優先級、黏性事件、訂閱的線程模式、訂閱者方法等等信息的類架構

  • findSubscriberMethods

顧名思義,就是查找SubscribeMethods的方法。具體看看怎麼實現的app

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }

    if (ignoreGeneratedIndex) {
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
        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.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}
複製代碼

METHOD_CACHE是一個map集合,用於緩存。代碼中能夠看到若是讀取到有緩存,會當即返回。若是沒有緩存最後也會添加到緩存中。async

ignoreGeneratedIndex是一個boolean變量,默認值爲false。用於判斷是否忽略註解生成的MyEventBusIndex,由於咱們一般都是使用單例獲取Evenbus實例,因此不會有MyEventBusIndex。而若是使用的是如下方式ide

EventBus.builder().addIndex(new MyEventBusIndex()).build()
複製代碼

會生成一個MyEventBusIndex文件。因爲不多用到我也不是很懂,而且這只是另一種使用方式而已,不會影響對核心源碼的理解。因此不作過多講解。所以ignoreGeneratedIndex這個值通常都爲false。所以先看看findUsingInfo這個方法

    • findUsingInfo
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {

   FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        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。這是一個內部類。封裝了定義訂閱者的公共方法。結構以下:

      • FindState
static class FindState {

        final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
        final Map<Class, Object> anyMethodByEventType = new HashMap<>();
        final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
        final StringBuilder methodKeyBuilder = new StringBuilder(128);

        Class<?> subscriberClass;
        Class<?> clazz;
        boolean skipSuperClasses;
        SubscriberInfo subscriberInfo;

        void initForSubscriber(Class<?> subscriberClass) {

...
        }

        boolean checkAdd(Method method, Class<?> eventType) {
...
        }

        private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
...
        }

        void moveToSuperclass() {
...
        }
    }

複製代碼

這種面向對象的封裝思想其實在自定義控件中若是對於一些變量有計算的咱們一樣能夠定義一個內部類作封裝,這樣的顯得代碼更有可讀性。getSubscriberInfo 方法是獲取訂閱者信息的,它的返回結果會影響後面if-else執行。經過源碼發現getSubscriberInfo裏面有三種返回結果

      • getSubscriberInfo
private SubscriberInfo getSubscriberInfo(FindState findState) {

    if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
        SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
        if (findState.clazz == superclassInfo.getSubscriberClass()) {
            return superclassInfo;
        }
    }
    if (subscriberInfoIndexes != null) {
        for (SubscriberInfoIndex index : subscriberInfoIndexes) {
            SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
            if (info != null) {
                return info;
            }
        }
    }
    return null;
}
複製代碼

返回superclassinfo和info,都表示有在EventBusBuilder裏面有配置MyEventBusIndex,正常不會走這裏。因此先看看返回null的狀況,這種狀況會執行findUsingReflectionInSingleClass 方法。

      • findUsingReflectionInSingleClass
private void findUsingReflectionInSingleClass(FindState findState) {
    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;
    }
    for (Method method : methods) {
        int modifiers = method.getModifiers();
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            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("@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中。這裏提一下的是,反射是寫架構經常用到的知識點,有人說反射是java語言的靈魂。

由於咱們一開始的目的是找到 SubscribeMethod類,這個類的做用上面說了是一個封裝訂閱者信息的類,而這時咱們已經經過反射拿到了訂閱者的全部方法和屬性。再到後面的添加緩存和讀取緩存其實只是爲了優化性能用到的了,因此到這裏查找訂閱者的訂閱方法算是完成了。

複製代碼

看回一開始的註冊方法,第二個關鍵方法是訂閱subscribe

  • subscribe
// Must be called in synchronized block
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) {
        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;
        }
    }

    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);
        }
    }
}
複製代碼

Subscription(訂閱對象)是根據Subscriber(訂閱者)和SubscribeMethod(訂閱方法)封裝的一個對象。

subscriptionByEventType是一個map對象,根據eventtype(事件類型)獲取Subscribptions集合,若是爲空從新建立並根據eventtype再保存到map中。

往下的for循環是根據訂閱方法的優先級插入訂閱對象的集合中,最後的 subscribeMethod.sticky 判斷是不是黏性事件。會發現未處理的黏性事件(先發送後註冊)實際上是在註冊方法裏遍歷取出處理的,而黏性事件的Set集合是由postSticky時保存的。

  • 簡單總結

訂閱方法作了兩件事情,第一是Subscription根據eventType封裝到subscriptionByEventType中,將SubscribeEvents根據 Subscriber封裝到 typesBySubscribe中,第二是黏性事件的處理。

複製代碼
  • 事件發送分析

  • post(Object)
/** Posts the given event to the event bus. */
public void post(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    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 {
            while (!eventQueue.isEmpty()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}
複製代碼

首先從PostingThredState中取出事件隊列集合並將當前的事件插入到集合中。最後循環交給postSingleEvent 處理

  • postSingleEvent
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));
        }
    }
}
複製代碼

eventInheritance表示是否向上查找事件的父類,能夠經過EventbusBuilder進行配置,默認值爲true。這時會經過lookupAllEventType查找父類父類事件並交給postSingleEventForEventType逐一處理。

  • postSingleEventForEventType
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        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 {
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
複製代碼

postSingleEventForEventType會同步取出訂閱對象。傳遞給PostingThreadState,PostingThreadState其實也只是一個變量的封裝。交給PostingThreadState後再把事件交給postToSubscription 處理

  • 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 {
                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);
    }
}
複製代碼

這個方法的做用是取出訂閱方法的線程模式,而後根據不一樣的線程模式作不一樣的處理。舉例MAIN,若是是主線程則經過反射直接運行訂閱方法,而若是不是主線程則須要經過mainThreadPost添加到主線程隊列中。mainThreadPost是HandlePost類型繼承自Handle。經過Handle對訂閱方法的線程進行切換。

  • 簡單總結

因此post方法通過一輪轉換最後就是經過訂閱方法找到線程模式,而後通過線程調度後再經過反射執行到對應的訂閱方法。

  • 心得

最後的反註冊方法毫無疑問就是去對應的容器中去除訂閱方法這裏不作過多講解,看到的朋友以爲寫的很差的地方歡迎指正,若是以爲還能夠的朋友能夠關注個人公衆號,但寫做對我來講純屬只是給本身一個時間靜靜學習的機會,即使沒有人承認我也會堅持


最開始堅持的地方,記錄學習與生活的點點滴滴
相關文章
相關標籤/搜索