Android——EventBus源碼分析

停停走走又幾天,斷斷續(xù)續(xù)這一年。時間還是如此之快,轉(zhuǎn)眼間又到了17年的末尾了。趁著這些天還能靜下心來看看代碼,趕緊記錄下自己的心得。
這一周,工作任務不多,總是想要看點什么但又不清楚該看啥,有些迷茫~不過既然自己還想要在Android路上繼續(xù)前行,那么請深入探索吧!
OK,閑話不多說。今天打算記錄下關于EventBus的原理以及如何實現(xiàn)。

1. 關于EventBus的使用

還依稀記得第一份工作中,那是還是個零基礎的菜鳥。修修補補那個項目,項目中就用到了EventBus,當時并沒有能力深入了解。當時版本的EventBus還沒有使用注解的形式,還需要自定義(記得是重寫onEvent方法還是啥)。
不過,現(xiàn)在EventBus的版本已經(jīng)到了3.+了,使用方法也和原來大不相同了。總體而言,代碼越來越簡潔,耦合度也越來越低了。說下簡單用法吧:

// 把當前的Activity或Fragment注冊到EventBus
// 相應的,需要在頁面銷毀是調(diào)用unRegister方法
EventBus.getDefault().register(this);
// 通過注解訂閱方法
// threadMode :方法執(zhí)行的線程
// sticky:是否接受粘性事件
// priority:優(yōu)先級
@Subscribe(threadMode = ThreadMode.POSTING, sticky = false, priority = 1)
public onEvent(EventMessage eventMessage){
    // do something
}

// 使用:通過調(diào)用post或postSticky方法使用,在訂閱的方法中執(zhí)行要執(zhí)行的代碼
EventBus.getDefault().post(EventMessage);

其實使用是很簡單的,不過3.x的版本中新加了Subscriber Index,使用起來比較麻煩,但是據(jù)說效率要高很多。這篇不算是教程,所以還是簡單的來看基本的使用吧。

2. EventBus的register/unregister過程

如果不看源碼的話,要我說這個注冊過程是把對象保存起來的過程,而取消注冊則是把對象釋放的過程。那么,源碼中如何實現(xiàn)的呢?下面進入源碼階段:

  1. EventBus.getDefault():
// 單例模式
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}

通過單例模式獲得EventBus對象,對象的創(chuàng)建過程:

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
// 保存事件參數(shù)類和Subscription List的Map
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
// 保存注冊對象和事件參數(shù)類
private final Map<Object, List<Class<?>>> typesBySubscriber;
// 粘性事件
private final Map<Class<?>, Object> stickyEvents;

public class EventBusBuilder {
    private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
    // 異常的設置
    boolean logSubscriberExceptions = true;
    boolean logNoSubscriberMessages = true;
    boolean sendSubscriberExceptionEvent = true;
    boolean sendNoSubscriberEvent = true;
    boolean throwSubscriberException;
    boolean eventInheritance = true;
    // 是否忽略生成的Index,默認為false,不忽略
    boolean ignoreGeneratedIndex;
    boolean strictMethodVerification;
    ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
    List<Class<?>> skipMethodVerificationForClasses;
    List<SubscriberInfoIndex> subscriberInfoIndexes;

    EventBusBuilder() {
    }
}

public EventBus() {
    this(DEFAULT_BUILDER);
}

EventBus(EventBusBuilder builder) {
    subscriptionsByEventType = new HashMap<>();
    typesBySubscriber = new HashMap<>();
    stickyEvents = new ConcurrentHashMap<>();
    // 主線程發(fā)送器
    mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
    // 后臺線程發(fā)送器
    backgroundPoster = new BackgroundPoster(this);
    // 異步線程發(fā)送器
    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;
}

EventBus是通過Builder形成創(chuàng)建,可以通過構(gòu)建EventBusBuilder來設置參數(shù),這里只記錄下默認的情況。這里面最重要的是三個Map對象:

  • subscriptionsByEventType:用來保存訂閱的方法參數(shù)類型和Subscription List
  • typesBySubscriber:用來保存訂閱者對象和被訂閱方法的參數(shù)類型
  • stickyEvents:粘性事件

當然,在創(chuàng)建EventBus時創(chuàng)建了三個不同類型的線程類型發(fā)送器,可以匹配注解中的threadMode字段。對象創(chuàng)建完成后需要調(diào)用register方法去將Activity/Fragment注冊:

Eventbus.java:
public void register(Object subscriber) {
    // 獲得注冊對象的class
    Class<?> subscriberClass = subscriber.getClass();
    // 查找訂閱者被訂閱的方法
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            // 訂閱
            subscribe(subscriber, subscriberMethod);
        }
    }
}

register方法,先要去查找訂閱者的方法,subscriberMethodFinder在創(chuàng)建對象的時候就被創(chuàng)建。接著調(diào)用subscriberMethodFinder.findSubscriberMethods(subscriberClass)方法,通過傳入注冊對象的class來查找其需要訂閱的方法:

SubscriberMethodFinder.java:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    // 從方法緩存中查找
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    // 找到直接返回
    if (subscriberMethods != null) {
        return subscriberMethods;
    }

    // 是否忽略生成的Index(高級用法會通過編譯時注解生成,這里不會)
    // ignoreGeneratedIndex默認為false,進入下面的代碼
    if (ignoreGeneratedIndex) {
        // 通過反射獲取方法
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
        // 通過subscriberInfo獲取方法
        subscriberMethods = findUsingInfo(subscriberClass);
    }
    // 如果沒有找到方法,拋出異常(常見的這個異常,沒有@Subscribe方法)
    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;
    }
}

首先從緩存中檢查是否擁有當前訂閱類,如果沒有則需要根據(jù)ignoreGeneratedIndex來判斷是否直接通過反射獲取。這里ignoreGeneratedIndex默認值為false,那么需要調(diào)用findUsingInfo

SubscriberMethodFinder.java:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    // 從池中或創(chuàng)建或使用
    FindState findState = prepareFindState();
    // 初始化
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        // 獲得subscriberInfo,
        findState.subscriberInfo = getSubscriberInfo(findState);
        // 如果沒有找到(不使用Subscribe Index的話也不會找到)
        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);
}

這里通過池取出或創(chuàng)建FindState對象,并且調(diào)用getSubscriberInfo查找是否有添加的SubscriberInfoIndex,這個是需要添加編譯器支持,并且在編譯器自動生成,需要手動添加的。這里默認的情況下是不存在的,所以就會進入findUsingReflectionInSingleClass又通過反射來調(diào)用(應該就是這里影響了效率,需要遍歷每個方法并且判斷。使用SubscriberInfoIndex 的話會標記類和訂閱的方法,所以速度會比這種方法快很多),接著調(diào)用moveToSuperclass獲得其父類(結(jié)束條件clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")),最后獲得這些方法并將findState回收:

SubscriberMethodFinder.java:
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();
        // 如果方法的修飾符是public并且不是 Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            // 獲得參數(shù)類型數(shù)組
            Class<?>[] parameterTypes = method.getParameterTypes();
            // 如果參數(shù)是一個
            if (parameterTypes.length == 1) {
                // 判斷該方法是否被Subscribe注解修飾
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                // 如果還有Subscribe注解,則將參數(shù)類型
                if (subscribeAnnotation != null) {
                    Class<?> eventType = parameterTypes[0];
                    // 檢驗下是否之前已經(jīng)添加了,具體下面分析
                    // 這里返回false,說明其父類也有這個方法,這里只需要子類的threadMode
                    if (findState.checkAdd(method, eventType)) {
                        // 添加后獲得threadMode,findState.subscriberMethods中添加SubscriberMethod對象
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                    }
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                // 拋出參數(shù)過多的異常
                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");
        }
    }
}

通過反射來獲取主要分為:

  1. 獲得類的方法
  2. 遍歷類,并且檢查:
    2.1 檢查方法修飾符是不是public
    2.2 檢查參數(shù)數(shù)量是不是等于1
    2.3 檢查方法是否還有Subscribe注解
    三個條件都滿足的話則需要檢查方法和參數(shù)類型是否已經(jīng)存在,是否此方法需要添加threadMode(我的理解是主要針對于子類覆蓋了父類的方法)
  3. 根據(jù)檢查結(jié)果來判斷是否添加threadMode
    關于checkAdd方法,這里面挺有意思的,看下吧:
SubscriberMethodFinder.java:
boolean checkAdd(Method method, Class<?> eventType) {
    // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
    // Usually a subscriber doesn't have methods listening to the same event type.
    // 分兩步檢查:
    // 1. 檢查參數(shù)類型是不是已經(jīng)存在
    // 2. 通過方法簽名(可以說是名稱拼接)來判斷
    Object existing = anyMethodByEventType.put(eventType, method);
    // 不存在接收該參數(shù)類型的方法
    if (existing == null) {
        return true;
    } else {
        // 存在接收該參數(shù)類型的方法,這里應該比較少見。應該就是不同的方法但是都有相同的參數(shù)
        if (existing instanceof Method) {
            if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                // Paranoia check
                throw new IllegalStateException();
            }
            // Put any non-Method object to "consume" the existing Method
            anyMethodByEventType.put(eventType, this);
        }
        return checkAddWithMethodSignature(method, eventType);
    }
}

SubscriberMethodFinder.java:
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
    methodKeyBuilder.setLength(0);
    methodKeyBuilder.append(method.getName());
    methodKeyBuilder.append('>').append(eventType.getName());
    // 名稱拼接 methodName>eventTypeName這種形式
    // 如果有相同方法名稱的話,那么需要將原methodClassOld保存
    // 這里可以理解為方法的重寫,子類重寫了可以手動調(diào)用(super.method()),而此時只保留子類中的方法
    String methodKey = methodKeyBuilder.toString();
    Class<?> methodClass = method.getDeclaringClass();
    Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
    if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
        // Only add if not already found in a sub class
        return true;
    } else {
        // Revert the put, old class is further down the class hierarchy
        subscriberClassByMethodKey.put(methodKey, methodClassOld);
        return false;
    }
}

從上面所講的可以知道,這個遍歷是從子類到其父類的一個遍歷過程。這里假設當前Activity中有兩個相同參數(shù)的方法a(Object)b(Object),并且其中一個方法是覆蓋了父類的a(Object)方法,而且父類的這個a(Object)方法也有注解@Subscribe
當?shù)谝淮翁砑恿四硞€類型的參數(shù),其添加值為b(Object),此時existing還為null
接著會添加a(Object)方法,此時existing存在,并且為第一次添加的方法b(Object),所以此時進入if (!checkAddWithMethodSignature((Method) existing, eventType))代碼塊,這里面第一次并沒有并且將b(Object)添加到subscriberClassByMethodKey中,此時subscriberClassByMethodKey保存了b(Object)。并且anyMethodByEventType.put(eventType, this)將findState對象作為該參數(shù)類型的值。通過這里來判斷有多個需要訂閱方法具有相同的參數(shù)類型。
接著又會調(diào)用checkAddWithMethodSignature方法,將a(Object)添加到subscriberClassByMethodKey中。
當添加其父類的a(Object)時,此時methodClassOld存在并且是當前methodClass的子類methodClassOld.isAssignableFrom(methodClass)返回false,將原methodClassOldmethodKey重新保存到subscriberClassByMethodKey中,并返回false,告訴上級,這里是其父類的方法,不需要根據(jù)這里的threadMode來添加。
這里判斷確實挺復雜,但是可以從這里看出寫一個框架的嚴謹性,所以情況都需要考慮到。
最后是將方法返回以及findState的回收:

SubscriberMethodFinder.java:
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
    // 創(chuàng)建List并返回
    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;
}

現(xiàn)在需要訂閱的方法已經(jīng)找到,接下來我們需要將方法訂閱:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    // 參數(shù)類型
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    // subscriptionsByEventType檢查是否存在
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        // 已經(jīng)存在的話,拋出異常
        if (subscriptions.contains(newSubscription)) {
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                    + eventType);
        }
    }
    
    //優(yōu)先級排序
    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;
        }
    }

    // 把對象和subscribedEvents保存起來
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    // 添加新的參數(shù)類型
    subscribedEvents.add(eventType);
    // 如果是粘性
    if (subscriberMethod.sticky) {
        // 構(gòu)造是默認為true
        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();
                // 參數(shù)類型是存儲的父類或者相同
                if (eventType.isAssignableFrom(candidateEventType)) {
                    Object stickyEvent = entry.getValue();
                    // 如果有值的話發(fā)送事件
                    // 意味著如果粘性事件的參數(shù)類型存在,并且對應的值也存在的話將發(fā)送事件
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        } else {
            Object stickyEvent = stickyEvents.get(eventType);
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }
}
  1. 訂閱的過程是一個對方法的遍歷過程,每個方法需要單獨訂閱。如果訂閱過程中該方法已經(jīng)訂閱,則拋出異常。
  2. 根據(jù)優(yōu)先級對訂閱的方法進行添加。
  3. 把訂閱者和訂閱方法的參數(shù)類型保存起來,需要時則可以根據(jù)類型來調(diào)用訂閱的方法。
  4. 檢查訂閱方法的是否為粘性方法,如果是的話,則會調(diào)用此方法。也就是說粘性方法在訂閱的時候會如果有粘性事件,那么會調(diào)用一次該訂閱的方法。

整個register的過程結(jié)束。其中最重要的還是將訂閱者和其訂閱方法的參數(shù)類型保存、訂閱方法參數(shù)類型和訂閱的方法保存以及對粘性方法的調(diào)用
看完了register的過程,我想對應unregister的過程也應該有了猜想。嗯~沒錯,就是將保存的數(shù)據(jù)移除即可。下面簡單看下:

public synchronized void unregister(Object subscriber) {
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        for (Class<?> eventType : subscribedTypes) {
            unsubscribeByEventType(subscriber, eventType);
        }
        // 將訂閱者訂閱的方法參數(shù)類型列表移除,防止內(nèi)存泄漏
        typesBySubscriber.remove(subscriber);
    } else {
        Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}

/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
    // 獲得保存此參數(shù)類型的Subscription
    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);
            // 如果Subscription的訂閱者就是取消訂閱這個對象,那么將其移除
            if (subscription.subscriber == subscriber) {
                subscription.active = false;
                subscriptions.remove(i);
                i--;
                size--;
            }
        }
    }
}

這些操作就是對訂閱者所訂閱的方法的移除,防止內(nèi)存泄漏。

3. post做了什么

訂閱完成后如果想要發(fā)送事件的話,一般使用

EventBus.getDefault().post(Object);

即可,這里看下post()的源碼:

public void post(Object event) {
    // 使用ThreadLocal來確保線程中局部變量PostingThreadState
    PostingThreadState postingState = currentPostingThreadState.get();
    // 獲取事件隊列
    List<Object> eventQueue = postingState.eventQueue;
    // 將最新的事件添加到隊尾
    eventQueue.add(event);
    
    // 如果當前沒有進行中的事件
    if (!postingState.isPosting) {
        // 設置當前線程是否為主線程
        postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
        // 設置正在發(fā)送狀態(tài)
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
            // 隊列循環(huán),將所有事件全部執(zhí)行完成
            while (!eventQueue.isEmpty()) {
                // 發(fā)送事件
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}
// 使用ThreadLocal來確保線程中局部變量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<Object>();
    boolean isPosting;
    boolean isMainThread;
    Subscription subscription;
    Object event;
    boolean canceled;
}

在調(diào)用post方法時,會通過使用ThreadLocal來確保線程中存在一個PostingThreadState局部變量,關于ThreadLocalHandler中也使用了ThreadLocal,有興趣可以看下。接著將此次事件加入到PostingThreadStateeventQueue中,并且隊列循環(huán),依次調(diào)用postSingleEvent發(fā)送處于隊首的事件。

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++) {
            // 對查找后的結(jié)果遍歷
            Class<?> clazz = eventTypes.get(h);
            // 為每個class發(fā)送事件
            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) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}

postSingleEvent中,根據(jù)參數(shù)類型在緩存中查找此參數(shù)類型的所有類包括接口(沒有的話會放入緩存,并將類返回),遍歷這些類,調(diào)用postSingleEventForEventType方法:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        // 根據(jù)事件類型獲得Subscription列表
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        // 遍歷
        for (Subscription subscription : subscriptions) {
            // 事件賦值以及訂閱賦值(postingState.subscription = subscription)
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            try {
                // 發(fā)送事件
                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;
}

根據(jù)傳入的參數(shù)類型從subscriptionsByEventType中獲取訂閱信息CopyOnWriteArrayList<Subscription> subscriptions,如果存在的話,對postingStateeventsubscription賦值,并且調(diào)用postToSubscription方法,最后將eventsubscription置為null

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    // 根據(jù)訂閱方法的threadMode來判斷如何執(zhí)行
    switch (subscription.subscriberMethod.threadMode) {
        // 默認,在哪個線程發(fā)送在哪個線程執(zhí)行
        case POSTING:
            invokeSubscriber(subscription, event);
            break;
        // 最終執(zhí)行在主線程中
        case MAIN:
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
                mainThreadPoster.enqueue(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);
    }
}

根據(jù)訂閱信息的線程模型來判斷在哪個線程執(zhí)行此訂閱方法,下面看一個BackgroundPoster后臺線程發(fā)送器吧:

BackgroundPoster.java:
final class BackgroundPoster implements Runnable {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    private volatile boolean executorRunning;

    BackgroundPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            // 入隊操作
            queue.enqueue(pendingPost);
            if (!executorRunning) {
                executorRunning = true;
                // 這里用到了線程池去執(zhí)行
                eventBus.getExecutorService().execute(this);
            }
        }
    }

    @Override
    public void run() {
        try {
            try {
                // 出隊,執(zhí)行隊列中所有的事件
                while (true) {
                    PendingPost pendingPost = queue.poll(1000);
                    if (pendingPost == null) {
                        synchronized (this) {
                            // Check again, this time in synchronized
                            // 再次檢測,如果還沒有則執(zhí)行結(jié)束
                            pendingPost = queue.poll();
                            if (pendingPost == null) {
                                executorRunning = false;
                                return;
                            }
                        }
                    }
                    // 存在PendingPost對象,調(diào)用EventBus去執(zhí)行訂閱者訂閱的方法
                    eventBus.invokeSubscriber(pendingPost);
                }
            } catch (InterruptedException e) {
                Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
    }

}

// executorService的默認值是下面的線程池
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

BackgroundPoster這里面維護了一個隊列,如果此發(fā)送器正在執(zhí)行,則將此次事件的PendingPost(將要發(fā)送的事件)對象加入隊列的尾部,在run方法中通過PendingPostQueue.poll()方法獲得第一個PendingPost對象并且調(diào)用eventBus.invokeSubscriber(pendingPost)去執(zhí)行訂閱的方法。

EventBus.java:
void invokeSubscriber(PendingPost pendingPost) {
    Object event = pendingPost.event;
    Subscription subscription = pendingPost.subscription;
    PendingPost.releasePendingPost(pendingPost);
    if (subscription.active) {
        // 如果沒有取消注冊,這個變量是volatile修飾,每次都會取最新值
        // 調(diào)用invokeSubscriber方法執(zhí)行訂閱的方法
        invokeSubscriber(subscription, event);
    }
}

EventBus.java:
void invokeSubscriber(Subscription subscription, Object event) {
    try {
        // 執(zhí)行訂閱者訂閱的方法,參數(shù)是event
        subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
    } catch (InvocationTargetException e) {
        handleSubscriberException(subscription, event, e.getCause());
    } catch (IllegalAccessException e) {
        throw new IllegalStateException("Unexpected exception", e);
    }
}

到這里后臺線程執(zhí)行任務到這里結(jié)束,其他的最終也是調(diào)用invokeSubscriber去執(zhí)行訂閱的方法,殊途同歸,不多贅述。

4. 總結(jié)

EventBus的源碼挺簡單的,雖然簡單,但是涉及的知識也挺多的,處理了同步問題,而且各種檢測也是很細致。自己也根據(jù)原理寫過一個框架,雖然代碼少,但是咱們問題多啊!這里還是再說下原理:將訂閱者和訂閱的方法以及方法參數(shù)保存,根據(jù)發(fā)送的事件類型即可獲得所有訂閱此參數(shù)類型的訂閱者和方法,最后可以通過Method.invok執(zhí)行方法
附美女一張~

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