Android事件總線 EventBus 2.4 源碼分析

EventBus簡介

本篇基于EventBus 2.4撰寫。

Android optimized event bus that simplifies communication between Activities, Fragments, Threads, Services, etc. Less code, better quality.

上面是從官方repo拉來的代碼,大致是說簡化的組件之間的交流通信,減少代碼,提高質量。

其實和EventBus最早是在qzone的代碼里認識的,空間內部有一個叫eventcenter的東西,曾經有優化過一些,當時看源碼實現的時候發現的原來是根據EventBus改的一個實現。大概就是把annotation的實現改成了接口實現,另外去掉了根據Event類型來找訂閱者的模式,完全通過Event的TYPE類型常量來判斷,register的時候直接指定對哪種TYPE感興趣,輔助的判斷則有事件發送者引用。這種實現見仁見智吧,雖然直接通過接口肯定是能提高性能的。這里要吐槽的是實現修改的時候,直接把很多對外的接口名字改掉了,何必呢。

EventBus的好處是顯而易見的,完全解耦了請求鏈之間的關系,避免了請求者被長持有,又比廣播更輕量,比LocalBroadcast則更強大,接口也簡單實用。缺點的話,像是各種Event的定義是一個工作量。

源碼分析 - 注冊(register)

EventBus.java:

private synchronized void register(Object subscriber, boolean sticky, int priority) {
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
    for (SubscriberMethod subscriberMethod : subscriberMethods) {
        subscribe(subscriber, subscriberMethod, sticky, priority);
    }
}

register的時候,大致就是去subscriber里面首先找到那些onEvent方法(目前實現仍然是根據onEvent這個前綴),尋找的時候會去判斷后綴,分為post線程、主線程、background線程,以及異步線程,官方repo提到這里之后在3.0可能會換成annotation的實現。

sticky參數是粘性事件概念,postSticky和registerSticky相對應,stickyEvent會記錄該EventType對應的最后一次postSticky的事件,這樣在registerSticky的時候,會立即檢查是否有之前post的事件,從而避免了某些事件去實現自己的緩存。應用場景大概就是某些activity/fragment感興趣的事件發生在創建前,這樣則可以避免必須實現緩存(當然事實上應用場景還是比較少的,因為大部分東西我們還是會在哪里記錄一下)

SubscriberMethod.java:

final class SubscriberMethod {
    final Method method;
    final ThreadMode threadMode;
    final Class<?> eventType;
    /** Used for efficient comparison */
    String methodString;
}

SubscriberMethod里面記錄了Method引用,線程模式(在findSubscriberMethods里拿到的),eventType,以及用來提高method.equals性能的methodString。

接著再看subscribe方法的實現,在register最后,對找到的所有方法都去執行了一遍subscribe
EventBus.java:

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
    Class<?> eventType = subscriberMethod.eventType;

    // CopyOnWriteArrayList就是個ImmutableArrayList, add/set等方法會返回一個新的ArrayList
    // subscriptionsByEventType是一個hashmap,key是事件類型,value則是訂閱者數組
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);


    Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);

    // 該eventType在map里還不存在,新建一下對應的subscription數組,放進去map
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<Subscription>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        // 重復注冊的時候拋出異常,這里如果應用如果覺得無傷大雅其實可以直接return
        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 || newSubscription.priority > subscriptions.get(i).priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    // typesBySubscriber是另一個map,顧名思義是以subscriber為key的一個map,被用在
    // 1) isRegistered(Object subscriber)方法加速判斷是否已注冊,用空間換時間
    // 2) unregister的時候直接可以拿到subscriber訂閱的所有eventType,然后去從map移除,避免需要遍歷所有eventType的map
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<Class<?>>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

    // 粘性事件的話,就去立刻找一下是否有之前post過的事件,有則立即post給該subscriber
    if (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);
        }
    }
}

源碼分析 - 發送事件(post)

再來看一下對應的post邏輯

EventBus.java:

/** Posts the given event to the event bus. */
public void post(Object event) {
    // 獲得當前post線程的狀態,實現貼在下面了,currentPostingThreadState是ThreadLocal<PostingThreadState>變量,每個線程get和set的都是單獨的一份數據
    PostingThreadState postingState = currentPostingThreadState.get();
    // 往事件隊列里面添加該event
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    // 如果當前不在posting事件
    if (!postingState.isPosting) {
        // 設置是否在主線程
        postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
        // 設置當前正在post事件
        postingState.isPosting = true;
        // canceled狀態,拋出異常
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
            // 循環post從eventQueue里面拿出來的event
            while (!eventQueue.isEmpty()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            // 置位回去
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}

final static class PostingThreadState {
    final List<Object> eventQueue = new ArrayList<Object>();
    boolean isPosting;
    boolean isMainThread;
    Subscription subscription;
    Object event;
    // 可以通過cancelEventDelivery去取消事件傳遞
    boolean canceled;
}

// 單個事件的post處理
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    // 繼承鏈處理,比如Event本身的父類的subscriber也會收到,getDefault的時候默認為true。
    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) {
            Log.d(TAG, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            // 沒想到還有這種邏輯吧,沒找到訂閱者的,則會發送一個NoSubscriberEvent出去
            post(new NoSubscriberEvent(this, event));
        }
    }
}

// 對特定的event去post單個事件
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 {
                // 結果實際post還在這個方法內實現
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } finally {
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            // 如果cancel了,則不再繼續傳遞事件
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
}

// 具體的事件分發
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    // 這里就是EventBus的一個很強大的功能了,根據訂閱者的訂閱方法監聽線程去處理
    // 如果post和監聽方法在同一個線程則立即invoke對應方法
    // 否則會去入隊列到對應線程handler進行處理
    switch (subscription.subscriberMethod.threadMode) {
        case PostThread:
            invokeSubscriber(subscription, event);
            break;
        case MainThread:
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case BackgroundThread:
            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);
    }
}

End

大致就講了一下register和post這一對比較常用的接口,其他還有一些實現像是EventBusBuilder,SubscriberException,cancelEventDelivery,AsyncExecutor就不在這里進行贅述,之后可能會對AsyncExecutor單獨開一篇講一下,另外也會對otto的實現做一下分析。

原文見:http://blog.zhaiyifan.cn/2015/08/20/EventBus%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容