[搞定開源]第五篇 EventBus3.1.1原理

Android開源項目原理系列
[搞定開源] 第一篇 okhttp 3.10原理
[搞定開源] 第二篇 okio 1.14原理
[搞定開源] 第三篇 retrofit 2.4.0與設計模式
[搞定開源] 第四篇 手動實現RxJava2線程切換

EventBus是Android的發佈訂閱開源庫,優勢和使用方法不用說了,簡單過一下源碼,瞭解核心原理。

當前EventBus最新版本是3.1.1,和前代相比,方法名可以自定義,新增註解支持。

EventBus構造函數

優秀開源庫的基本特徵是,常用功能用一個入口類就可以玩轉,EventBus也是。

public static EventBus getDefault() {
  if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}

獲取EventBus對象常規使用getDefault,典型的Double Check獲取單例。如果有特別需求,可以通過EventBusBuilder,典型的建造者模式。

EventBus構造函數創建了十多個對象,關注其中的:

EventBus(EventBusBuilder builder) {
    logger = builder.getLogger();
    //...
    mainThreadSupport = builder.getMainThreadSupport();
    mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
    backgroundPoster = new BackgroundPoster(this);
    //...
}

Log

EventBus的日誌打印使用Logger接口,已經實現了:

  • AndroidLogger
  • JavaLogger
  • SystemOutLogger
static final boolean ANDROID_LOG_AVAILABLE;

static {
    boolean android = false;
    try {
        android = Class.forName("android.util.Log") != null;
    } catch (ClassNotFoundException e) {
        // OK
    }
    ANDROID_LOG_AVAILABLE = android;
}

public static boolean isAndroidLogAvailable() {
    return ANDROID_LOG_AVAILABLE;
}

對於Android,直接嘗試獲取Log類,同時用來判斷當前是否Android。

獲取Android的UI線程

EventBus如何讓事件執行在Android的UI線程呢?

MainThreadSupport getMainThreadSupport() {
    if (mainThreadSupport != null) {
        return mainThreadSupport;
    } else if (Logger.AndroidLogger.isAndroidLogAvailable()) {
        Object looperOrNull = getAndroidMainLooperOrNull();
        return looperOrNull == null ? null :
                new MainThreadSupport.AndroidHandlerMainThreadSupport((Looper) looperOrNull);
    } else {
        return null;
    }
}

Object getAndroidMainLooperOrNull() {
    try {
        return Looper.getMainLooper();
    } catch (RuntimeException e) {
        // Not really a functional Android (e.g. "Stub!" maven dependencies)
        return null;
    }
}

這裏用到了上面判斷Android的方法,如果是,創建AndroidHandlerMainThreadSupport,保存了Looper.getMainLooper()。很熟悉吧,這就是Android的UI線程Looper。

Poster

EventBus的event可以指定運行在UI線程或者後臺線程,靠的就是Poster,下文會說到,透露一丟丟:

  • UI線程:MainLooper
  • 後臺線程:線程池

註冊/取消註冊

想要接收EventBus發送的事件,對象必須先註冊,停止接收時取消註冊。註冊過程,本質是通知EventBus查找對象中的訂閱方法。

Subscribe註解

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent4Main(MsgEvent event) {}

EventBus3之後,訂閱方法名改爲自定義,通過註解描述。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;
    boolean sticky() default false;
    int priority() default 0;
}

除了基礎的ThreadMode線程模型,還可以定義粘性事件和優先級。

註解信息,在註冊過程最後由SubscriberMethod類描述。

註冊過程

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

註冊時傳入訂閱對象,兩部操作:

  1. 分析訂閱對象的類,SubscriberMethodFinder從中查找訂閱方法,包裝爲SubscriberMethod。
  2. 調用subscribe,數據轉化爲EventBus內部的兩個Map。
List<SubscriberMethod>
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    //1
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }

    //2
    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 {
        //3
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}
  1. 查找目標類的訂閱方法前,先查找緩存METHOD_CACHE,用的是ConcurrentHashMap,因爲要處理多線程。
Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>()
  1. 默認創建的EventBus對象ignoreGeneratedIndex=false,先看findUsingInfo
  2. 獲取List成功後,保存在緩存METHOD_CACHE
SubscriberMethodFinder.FindState

上面第二步重點是使用SubscriberMethodFinder的內部類FindState執行查找過程:

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    //1
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    //2
    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 {
            //3
            findUsingReflectionInSingleClass(findState);
        }
        //4
        findState.moveToSuperclass();
    }
    //5
    return getMethodsAndRelease(findState);
}

第一步是調用prepareFindState獲取FindState:

private static final int POOL_SIZE = 4;
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
    
private FindState prepareFindState() {
    synchronized (FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            FindState state = FIND_STATE_POOL[i];
            if (state != null) {
                FIND_STATE_POOL[i] = null;
                return state;
            }
        }
    }
    return new FindState();
}

FindState的創建不是直接new,而是嘗試從FIND_STATE_POOL中獲取(最多保存4個),目測是享元模式,沒有才new對象。

第二步進入循環,從當前類開始,遍歷父類查找訂閱方法。

這裏涉及EventBus3新特性,運行時反射查找方法當然會慢點,可以使用Subscriber Index在編譯期操作加速,具體不展開,直接看else部分。

第三步的findUsingReflectionInSingleClass,使用反射查找,細節不用展開。

第四步將當前查找目標切換到父類,繼續循環查找。

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
    List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
    findState.recycle();
    synchronized (FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            if (FIND_STATE_POOL[i] == null) {
                FIND_STATE_POOL[i] = findState;
                break;
            }
        }
    }
    return subscriberMethods;
}

第五步清理FindState,放回FIND_STATE_POOL,下次繼續利用。

subscribe

register函數對每個SubscriberMethod執行subscribe,注意這裏synchronized當前EventBus對象。

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);

    //... 粘性事件略
}

subscribe不外乎是對數據的挪騰,目的是構建subscriptionsByEventType和typesBySubscriber兩個Map:

subscriptionsByEventType

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;

Subscription包裝了訂閱對象和訂閱方法,因此Map的key/value對應如下:

event類型 -> List<訂閱對象,訂閱方法>

表示了event由哪些對象的哪個方法訂閱了。

typesBySubscriber

private final Map<Object, List<Class<?>>> typesBySubscriber;

typesBySubscriber簡單些,表示訂閱對象訂閱了哪些event類型:

訂閱對象 -> List<event類型>

取消註冊

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());
    }
}

取消註冊的邏輯很簡單,將當前訂閱者的信息從subscriptionsByEventType和typesBySubscriber中移除。

發送

PostingThreadState(每個線程一份)

EventBus發送event調用post方法,首先來認識EventBus核心的內部類PostingThreadState。

private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
    @Override
    protected PostingThreadState initialValue() {
        return new PostingThreadState();
    }
};
    
final static class PostingThreadState {
    final List<Object> eventQueue = new ArrayList<>();
    boolean isPosting;
    boolean isMainThread;
    Subscription subscription;
    Object event;
    boolean canceled;
}

PostingThreadState包含eventQueue和一些標誌變量,通過ThreadLocal在每個線程中維護一個對象(ThreadLocal的initialValue方法初始創建PostingThreadState對象)。

沒有深奧的玄妙,看數據結構大概可以知道,EventBus發送原理是將event入隊在線程上的PostingThreadState,再查找訂閱者發送。

event進出隊

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;
        }
    }
}

發送event的邏輯很明確,取出當前線程的PostingThreadState,將event入隊。當不在發送狀態時,循環調用postSingleEvent發送所有event。

發送event

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    //1
    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);
    }
    //2
    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));
        }
    }
}

第一部分先收集event的類型,eventInheritance在EventBus默認構造函數裏爲true,表示會向上遍歷event的父類。然後調用postSingleEventForEventType執行發送過程,得到發送結果subscriptionFound。

第二部分,如果找不到訂閱者,EventBus將會發送NoSubscriberEvent這個事件,業務上可以接收處理。

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根據event類型從subscriptionsByEventType獲取所有訂閱者,對每個訂閱者執行postToSubscription。

訂閱方法各有執行線程的要求,postToSubscription其實就是對各個線程模型的分開處理,下面分析常用的POSTING、MAIN和BACKGROUND。

POSTING
void invokeSubscriber(Subscription subscription, Object event) {
    try {
        subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
    } catch (InvocationTargetException e) {
        handleSubscriberException(subscription, event, e.getCause());
    } catch (IllegalAccessException e) {
        throw new IllegalStateException("Unexpected exception", e);
    }
}

POSTING,在哪個線程發送,就在哪個線程處理事件,直接反射invoke。

MAIN
 if (isMainThread) {
    invokeSubscriber(subscription, event);
} else {
    mainThreadPoster.enqueue(subscription, event);
}

如果當前已經在UI線程,直接調用invokeSubscriber,否則調用mainThreadPoster將event入隊。

前面分析了AndroidHandlerMainThreadSupport保存了UI線程的Looper,通過Looper創建HandlerPoster,mainThreadPoster正是HandlerPoster的對象。有了UI線程的Handler,接下來的都是順理成章的代碼。

public void enqueue(Subscription subscription, Object event) {
    PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
    synchronized (this) {
        queue.enqueue(pendingPost);
        if (!handlerActive) {
            handlerActive = true;
            if (!sendMessage(obtainMessage())) {
                throw new EventBusException("Could not send handler message");
            }
        }
    }
}

訂閱者信息Subscription和event包裝爲PendingPost,通過next字段,多個PendingPost構建隊列PendingPostQueue。

@Override
public void handleMessage(Message msg) {
    boolean rescheduled = false;
    try {
        long started = SystemClock.uptimeMillis();
        while (true) {
            PendingPost pendingPost = queue.poll();
            if (pendingPost == null) {
                synchronized (this) {
                    // Check again, this time in synchronized
                    pendingPost = queue.poll();
                    if (pendingPost == null) {
                        handlerActive = false;
                        return;
                    }
                }
            }
            eventBus.invokeSubscriber(pendingPost);
            long timeInMethod = SystemClock.uptimeMillis() - started;
            if (timeInMethod >= maxMillisInsideHandleMessage) {
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
                rescheduled = true;
                return;
            }
        }
    } finally {
        handlerActive = rescheduled;
    }
}

通過發送空message,切換線程到UI線程,從隊列中取出event調用invokeSubscriber。注意到,在UI線程有執行時長限制,超時會拋出異常。

BACKGROUND
if (isMainThread) {
    backgroundPoster.enqueue(subscription, event);
} else {
    invokeSubscriber(subscription, event);
}
  • 如果事件在UI線程中發佈,那麼該事件就會在新的線程中運行
  • 如果事件在子線程中發佈,那麼該事件直接在發佈事件的線程中執行

BackgroundPoster處理事件在新線程中的運行,同樣的創建PendingPost併入隊。

public void enqueue(Subscription subscription, Object event) {
    PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
    synchronized (this) {
        queue.enqueue(pendingPost);
        if (!executorRunning) {
            executorRunning = true;
            eventBus.getExecutorService().execute(this);
        }
    }
}

線程池從EventBus對象獲取,看定義,是一個線程數幾乎無限制的CachedThreadPool。

private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();

最終在線程池的執行過程也是PendingPost出隊,調用invokeSubscriber。(代碼嵌套很深!)

@Override
public void run() {
    try {
        try {
            while (true) {
                PendingPost pendingPost = queue.poll(1000);
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            executorRunning = false;
                            return;
                        }
                    }
                }
                eventBus.invokeSubscriber(pendingPost);
            }
        } catch (InterruptedException e) {
            eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
        }
    } finally {
        executorRunning = false;
    }
}

後記

EventBus源碼沒有太難的地方,快速通過。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章