1. 程式人生 > >EventBus3.0原始碼解析-02

EventBus3.0原始碼解析-02

EventBus3.0原始碼解析-01這篇文章簡單的介紹了EventBus的使用流程,我們知道EventBus有三個主要入口方法,分別為

  • EventBus.getDefault().register(Object);
    
  • EventBus.getDefault().unregister(Object);
    
  • EventBus.getDefault().post(Object);
    

本文將通過register入口方法及其邏輯,分析EventBus原始碼。

EventBus的register方法實現如下:

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

register函式主要實現了兩個功能,1、將訂閱類中所有訂閱方法收集;2、按一定規則儲存收集的資訊。

訂閱方法收集

訂閱方法收集主要由SubscriberMethodFinder類的findSubscriberMethods函式實現,我們看下其具體實現:

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

1、如果引數中的subscriberClass類之前註冊過,那麼直接從METHOD_CACHE快取中獲取資訊返回。

2、根據ignoreGeneratedIndex判斷是否要根據註解處理器在編譯期生成的索引檔案來尋找訂閱方法資訊,該方式是從EventBus3.0中引入的,目的是為了提高EventBus的整體效率。如果ignoreGeneratedIndex為true,則使用反射來尋找訂閱方法資訊,否則,先根據生成的索引檔案來尋找訂閱方法資訊,索引檔案中找不到,再使用反射尋找。

3、如果找到的訂閱方法資訊非空,則將這些資訊快取在METHOD_CACHE中。

第1、3步沒有什麼好講的內容,主要的內容在第2步。由於根據索引檔案尋找訂閱方法資訊的方式還涉及註解處理器,稍微複雜,所以會單獨開一篇文章講這塊。本文主要介紹通過反射來尋找訂閱方法資訊的方式。

我們來看一下findUsingReflection的具體實現:

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    // 遍歷訂閱類以及其父類
    while (findState.clazz != null) {
        findUsingReflectionInSingleClass(findState);
        findState.moveToSuperclass();
    }
    return getMethodsAndRelease(findState);
}
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();
}

prepareFindState()方法會嘗試從快取中尋找是否有現存的FindState物件,如果有,則直接使用快取的FindState物件,否則,直接new一個FindState物件。

void initForSubscriber(Class<?> subscriberClass) {
	// 儲存訂閱類資訊
    this.subscriberClass = clazz = subscriberClass;
    // 是否忽略父類
    skipSuperClasses = false;
    subscriberInfo = null;
}

initForSubscriber方法就是簡單的初始化了一些引數。

接下來我們看下核心方法findUsingReflectionInSingleClass的實現:

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // getDeclaredMethods()方法的效率要優於getMethods()
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        methods = findState.clazz.getMethods();
        findState.skipSuperClasses = true;
    }
    for (Method method : methods) {
        // 獲取方法的修飾符, 比如public, static等修飾符
        int modifiers = method.getModifiers();
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) 			{
        	// 獲取方法的引數資訊
            Class<?>[] parameterTypes = method.getParameterTypes();
            // 方法只有一個引數
            if (parameterTypes.length == 1) {
                // 獲取方法的@Subscribe註解資訊
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                // 如果方法有@Subscribe註解資訊
                if (subscribeAnnotation != null) {
                    Class<?> eventType = parameterTypes[0];
                    // 檢查當前method是否是有效的訂閱方法
                    if (findState.checkAdd(method, eventType)) {
                        // 如果當前method是有效的訂閱方法,則把該訂閱方法收集起來
                        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");
        }
    }
}

findUsingReflectionInSingleClass方法加了詳細的註解,大多數邏輯都能直接看程式碼註釋弄明白。這裡主要要介紹的是findState.checkAdd方法,checkAdd的實現如下:

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.
    Object existing = anyMethodByEventType.put(eventType, method);
    if (existing == null) {
        return true;
    } else {
        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);
    }
}

考慮到同一個類裡面通常只會有一個方法監聽同一類事件,為了效率,所以EventBus先根據事件型別去匹配同一個類中是否已經有新增過的方法監聽同一類事件,如果沒有,則直接認為該方法為有效訂閱方法,如果之前已經新增過方法監聽同一類事件,則通過checkAddWithMethodSignature來判斷該方法是否為有效訂閱方法。checkAddWithMethodSignature的實現如下:

private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
    methodKeyBuilder.setLength(0);
    methodKeyBuilder.append(method.getName());
    methodKeyBuilder.append('>').append(eventType.getName());

    String methodKey = methodKeyBuilder.toString();
    Class<?> methodClass = method.getDeclaringClass();
    Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
    // methodClassOld 是否是 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;
    }
}

根據一定規則計算出方法的唯一特徵名稱,如果具有該特徵名稱的方法之前還沒有新增過,則認為當前方法為有效訂閱方法。否則,如果之前新增過的方法所在的類是當前方法所在類的父類或者介面,考慮到子類重寫,認為當前方法為有效訂閱方法。否則認為當前方法為非有效訂閱方法。

通過上面的多次呼叫findUsingReflectionInSingleClass方法,我們已經把有效的訂閱資訊收集到了findState的subscriberMethods變數中。

儲存所有訂閱資訊

我們再回到findUsingReflection方法,接著會呼叫getMethodsAndRelease(findState)方法,我們看看它的實現:

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放入到快取中,然後返回剛剛收集到的有效訂閱資訊。

現在我們回到register方法,我們收集到了有效的訂閱資訊,接著會迴圈呼叫subscribe(subscriber, subscriberMethod)方法,我們看看這個方法的實現:

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

這個函式的程式碼雖然很長,但是其只做了三件事情,1、將訂閱資訊以事件型別為key儲存在subscriptionsByEventType變數中(方便post方法呼叫的時候,以事件為key尋找訂閱資訊);2、將事件型別以訂閱類為key儲存在typesBySubscriber變數中(方便unregister方法呼叫的時候,以訂閱類為key尋找事件型別,然後根據subscriptionsByEventType變數找到訂閱資訊);3、處理sticky事件邏輯。關於第3點,考慮到本文篇幅,我將另外開一篇文章進行分析。

總結

所以通過呼叫register方法,最終訂閱方法資訊會被最終儲存在subscriptionsByEventType變數和typesBySubscriber變數中。

如果你喜歡,感謝給個贊。如果有什麼問題,歡迎評論。