EventBus原始碼解讀詳細註釋(1)register的幕後黑手

王世暉發表於2016-03-17

[EventBus原始碼分析(一):入口函式提綱挈領(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51802172)
[EventBus原始碼分析(二):register方法儲存事件的訂閱者列表(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51819508)
[EventBus原始碼分析(三):post方法釋出事件【獲取事件的所有訂閱者,反射呼叫訂閱者事件處理方法】(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51821143)
[EventBus原始碼分析(四):執行緒模型分析(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51832001)
[EventBus原始碼解讀詳細註釋(1)register的幕後黑手](http://blog.csdn.net/wangshihui512/article/details/50914817)
[EventBus原始碼解讀詳細註釋(2)MainThread執行緒模型分析](http://blog.csdn.net/wangshihui512/article/details/50934012)
[EventBus原始碼解讀詳細註釋(3)PostThread、MainThread、BackgroundThread、Async四種執行緒模式的區別](http://blog.csdn.net/wangshihui512/article/details/50935729)
[EventBus原始碼解讀詳細註釋(4)register時重新整理的兩個map](http://blog.csdn.net/wangshihui512/article/details/50938663)
[EventBus原始碼解讀詳細註釋(5)事件訊息繼承性分析 eventInheritance含義](http://blog.csdn.net/wangshihui512/article/details/50947102)
[EventBus原始碼解讀詳細註釋(6)從事件釋出到事件處理,究竟發生了什麼!](http://blog.csdn.net/wangshihui512/article/details/50949960)



register(this)就是去當前類,遍歷所有的方法,按照事件處理方法的命名規則約束(onEvent開頭,只有一個引數,非static,非abstract的public方法)過濾出事件處理方法然後進行儲存。然後迴圈遍歷當前類的父類,做同樣的處理。

通過閱讀原始碼發現如果子類沒有覆寫父類的事件處理方法,那麼父類的事件處理方法將會加入事件處理方法列表。

private synchronized void register(Object subscriber, boolean sticky, int priority) {
    /*getClass():* Returns the unique instance of  Class that represents this object's class.*/
    /*過濾出訂閱者的所有事件處理方法(包括父類的),以列表形式返回*/
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
    for (SubscriberMethod subscriberMethod : subscriberMethods) {
        /*遍歷,對訂閱者的每一個事件處理方法進行儲存/
        subscribe(subscriber, subscriberMethod, sticky, priority);
    }
}


/**
 * @param subscriberClass
 * @return
 */
/*訂閱者 subscriberClass:註冊EventBus的Activity或其他元件,EventBus.getDefault().register(this)中的this*/
/*方法findSubscriberMethods的作用是過濾出訂閱者的所有事件處理方法方法,以列表形式返回*/
/*在EventBus中的觀察者通常有四種訂閱函式(就是某件事情發生被呼叫的方法)
1、onEvent
2、onEventMainThread
3、onEventBackground
4、onEventAsync
這四種訂閱函式都是使用onEvent開頭的,它們的功能稍有不同,在介紹不同之前先介紹兩個概念:
告知觀察者事件發生時通過EventBus.post函式實現,這個過程叫做事件的釋出,觀察者被告知事件發生叫做事件的接收,是通過下面的訂閱函式實現的。
onEvent:如果使用onEvent作為訂閱函式,那麼該事件在哪個執行緒釋出出來的,onEvent就會在哪個執行緒中執行,也就是說釋出事件和接收事件執行緒在同一個執行緒。
使用這個方法時,在onEvent方法中不能執行耗時操作,如果執行耗時操作容易導致事件分發延遲。
onEventMainThread:如果使用onEventMainThread作為訂閱函式,那麼不論事件是在哪個執行緒中釋出出來的,都會在主執行緒中呼叫事件處理方法,這個在Android中是非常有用的,因為在Android中只能在UI執行緒中更新UI,所以在onEvnetMainThread方法中是不能執行耗時操作的。
onEvnetBackground:如果使用onEventBackgrond作為訂閱函式,那麼如果事件是在UI執行緒中釋出出來的,那麼onEventBackground就會在子執行緒中執行,如果事件本來就是子執行緒中釋出出來的,那麼onEventBackground函式直接在該子執行緒中執行。
onEventAsync:使用這個函式作為訂閱函式,那麼無論事件在哪個執行緒釋出,都會建立新的子執行緒在執行onEventAsync.*/
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    /*getName():Returns the name of the class represented by this Class 返回訂閱者的類名,比如xxx.xxx.xxx.MainActivity*/
    String key = subscriberClass.getName();
    List<SubscriberMethod> subscriberMethods;
    /* private static final Map<String, List<SubscriberMethod>> methodCache = new HashMap<String, List<SubscriberMethod>>();*/
    /*方法快取就是一個靜態最終的HashMap,key是訂閱者的類名,value是訂閱者事件處理方法的列表*/
    /*先讀快取,看能否命中*/
    synchronized (methodCache) {
        subscriberMethods = methodCache.get(key);
    }
    /*如果快取命中,直接返回快取中的事件處理方法列表*/
    if (subscriberMethods != null) {
        return subscriberMethods;
    }
    /*快取不命中,開始按照事件處理方法的命名規則進行過濾*/
    subscriberMethods = new ArrayList<SubscriberMethod>();
    Class<?> clazz = subscriberClass;
    /*eventTypesFound存放的是事件處理方法的過濾結果,key就是過濾出來的事件處理方法,value就是宣告瞭這個事件處理方法的訂閱者的Class*/
    /*因為是迴圈處理訂閱者的所有父類的,所以如果子類覆寫了父類的事件處理方法,那麼父類和子類的事件處理方法名稱和引數完全一致
    * 而事件處理方法名稱和引數是作為eventTypeFound的key儲存的,key必須唯一*/
    HashMap<String, Class> eventTypesFound = new HashMap<String, Class>();
    /*注意這個StringBuilder類是在迴圈外建立的,在迴圈內建立就太沖動了*/
    StringBuilder methodKeyBuilder = new StringBuilder();
    /*迴圈的最後一句話:clazz = clazz.getSuperclass(); 表示不僅要找當前類的事件處理方法,還要找所有父類的事件處理方法*/
    while (clazz != null) {
        String name = clazz.getName();
        /*如果類名是以java. javax. android. 為字首,說明是系統類,不處理系統的類,直接結束迴圈,這是出於效能的考慮*/
        if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
            // Skip system classes, this just degrades performance
            break;
        }
        // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
        /*從EventBus2.2版本開始要求事件處理方法必須是公開的方法*/
        try {
            // This is faster than getMethods, especially when subscribers a fat classes like Activities
            /*
            getDeclaredMethods():返回所有方法,不僅僅是公開的,但不包括繼承的方法
            getMethods():返回所有公開方法,包括繼承來的方法,如果訂閱者是個比較複雜的類,那麼getDeclaredMethods()速度要快於getMethods()
            getDeclaredMethods():Returns an array containing  Method objects
            for all methods declared in the class represented by this Class. */
            Method[] methods = clazz.getDeclaredMethods();
            /*對訂閱者的所有方法進行過濾,得到事件處理方法列表*/
            filterSubscriberMethods(subscriberMethods, eventTypesFound, methodKeyBuilder, methods);
        } catch (Throwable th) {
            th.printStackTrace();
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            Method[] methods = subscriberClass.getMethods();
            subscriberMethods.clear();
            eventTypesFound.clear();
            filterSubscriberMethods(subscriberMethods, eventTypesFound, methodKeyBuilder, methods);
            break;
        }
        /*獲取父類,對訂閱者的父類進行同樣的處理*/
        clazz = clazz.getSuperclass();
    }
    /*如果元件註冊了EventBus,就成為了訂閱者,訂閱者不能沒有事件處理方法,否則丟擲異常*/
    if (subscriberMethods.isEmpty()) {
        throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
                + ON_EVENT_METHOD_NAME);
    } else {
        /*過濾一次事件處理方法還是挺麻煩的,所以還是加入快取吧*/
        synchronized (methodCache) {
            methodCache.put(key, subscriberMethods);
        }
        return subscriberMethods;
    }
}

/**對訂閱者的所有方法進行過濾,得到事件處理方法列表
 * @param subscriberMethods 返回的事件處理方法列表
 * @param eventTypesFound  過濾出的時間處理方法
 * @param methodKeyBuilder
 * @param methods 所有方法
 */
private void filterSubscriberMethods(List<SubscriberMethod> subscriberMethods,
                                     HashMap<String, Class> eventTypesFound, StringBuilder methodKeyBuilder,
                                     Method[] methods) {
    /*遍歷每個方法,對每個方法進行處理,判斷是否為事件處理方法*/
    for (Method method : methods) {
        /*得到方法名*/
        String methodName = method.getName();
        /*private static final String ON_EVENT_METHOD_NAME = "onEvent";*/
        /*如果方法名以onEvent開頭就接著處理*/
        if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {
            /*獲取該方法的修飾符*/
            int modifiers = method.getModifiers();
            /*獲取宣告瞭這個方法的類*/
            /*getDeclaringClass():returns the class that declares this method.*/
            Class<?> methodClass = method.getDeclaringClass();
            /* private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;*/
            /*如果是非abstract、static的public方法,接著處理*/
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                /*獲取該方法的引數型別列表*/
                Class<?>[] parameterTypes = method.getParameterTypes();
                /*事件處理方法約定只有一個引數*/
                if (parameterTypes.length == 1) {
                    /*根據方法名判斷執行緒型別*/
                    ThreadMode threadMode = getThreadMode(methodClass, method, methodName);
                    /*如果這個方法不是事件處理方法,跳出這次迴圈*/
                    if (threadMode == null) {
                        continue;
                    }
                    /*獲取引數型別,事件處理函式只有一個引數,所以取引數列表的第一項*/
                    Class<?> eventType = parameterTypes[0];
                    methodKeyBuilder.setLength(0);
                    methodKeyBuilder.append(methodName);
                    methodKeyBuilder.append('>').append(eventType.getName());
                    /*舉個例子,如果事件處理函式為 onEvent(AutoRefreshMsg msg)
                    * 那麼methodKey就為onEvent>AutoRefreshMsg*/
                    String methodKey = methodKeyBuilder.toString();
                    /*HashMap<String, Class> eventTypesFound 是傳入的引數*/
                    /*eventTypesFound 中key為onEvent>AutoRefreshMsg這種格式的字串,value為宣告瞭該事件處理方法的類*/
                    /* HashMap : public V put(K key, V value)
                     * Maps the specified key to the specified value.
                     * @param key the key.
                     * @param value the value.
                     * @return the value of any previous mapping with the specified key or
                     *         code null if there was no such mapping.
                     *         如果HashMap之前沒有這樣的K V對,或者說是這樣的K V對第一次新增到HashMap,就返回null
                     */
                    /* methodClassOld表示之前是否已經儲存過了定義了這個事件處理方法的類,如果沒有,那麼methodClassOld==null*/
                    /*注意HashMap的put方法的返回值,返回對應k之前儲存的value或者null如果HashMap*/
                    /*注意對訂閱者事件處理方法的過濾是先過濾當前訂閱者,然後迴圈過濾當前訂閱者的父類*/
                    /*所以如果子類覆寫了父類的事件處理方法,那麼肯定先處理子類
                    * 先把此事件處理方法和子類的類Class儲存在eventTypesFound
                    * 然後再下一次迴圈處理父類的時候,呼叫map的put方法,因為子類覆寫了父類的事件處理方法
                    * 所以key值存在,put方法返回之前對用的value,也就是子類的Class*/
                    Class methodClassOld = eventTypesFound.put(methodKey, methodClass);
                    /* public boolean isAssignableFrom(Class<?> c)
                    isAssignableFrom:Can c  be assigned to this class */
                    /* Class<?> methodClass = method.getDeclaringClass();*/
                    /*注意這裡或運算的短路性質*/
                    /* instanceof 針對例項  isAssignableFrom針對class物件*/
                    /* new A() instanceof B 判斷A物件是否是B類的子類或B類的例項*/
                    /* B.class.isAssignableFrom(A.class) A類是否可以賦值給B類,也就是判斷B是不是A的父類*/
 
                    /*對已事件處理方法,子類和父類的都一樣,所以只把最底層的訂閱者的事件處理方法加入事件處理方法列表
                    *  eventTypesFound需要儲存事件處理方法和Class類名,在子類、父類、父類的父類中,事件處理方法名只有把一個(覆寫)
                    *  Class名多個,規定只儲存最繼承層次最底層那個子類的Class作為value*/
                    /*如果子類沒有覆寫父類的事件處理方法,那麼就把父類的事件處理方法加入事件處理方法列表*/
                    if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
                        // Only add if not already found in a sub class
                        /*子類中沒找到才新增*/
                       /* List<SubscriberMethod> subscriberMethods 方法傳進來的引數*/
                        subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
                    } else {
                          /*HashMap<String, Class> eventTypesFound 是傳入的引數*/
                        /*eventTypesFound 中key為onEvent>AutoRefreshMsg這種格式的字串,value為宣告瞭該事件處理方法的類*/
                        // Revert the put, old class is further down the class hierarchy
                        /*注意是先處理子類後處理父類*/
                        eventTypesFound.put(methodKey, methodClassOld);
                    }
                }
            } else if (!skipMethodVerificationForClasses.containsKey(methodClass)) {
                Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + methodClass + "."
                        + methodName);
            }
        }
    }
}

/**
 * 根據方法名稱判斷執行緒型別
 * @param clazz 訂閱者
 * @param method
 * @param methodName 以onEvent開頭的非static、abstract的public方法
 * @return
 */
private ThreadMode getThreadMode(Class<?> clazz, Method method, String methodName) {
    /*private static final String ON_EVENT_METHOD_NAME = "onEvent";*/
    /*去掉方法名前邊的不變數onEvent,留下後邊的變數區分執行緒型別*/
    String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
    ThreadMode threadMode;
    /*四種執行緒模型*/
    if (modifierString.length() == 0) {
        /*onEvent:回撥函式和發起事件的函式會在同一個執行緒中執行*/
        threadMode = ThreadMode.PostThread;
    } else if (modifierString.equals("MainThread")) {
        /*onEventMainThread:回撥函式會在主執行緒中執行,這個在Android中非常有用,因為在Android中禁止在子執行緒中修改UI*/
        threadMode = ThreadMode.MainThread;
    } else if (modifierString.equals("BackgroundThread")) {
        /*onEventBackgroundThread:如果在主執行緒釋出事件,那麼在子執行緒中處理該事件;
        吐過在子執行緒釋出事件,那麼事件處理方法就在這個子執行緒執行。*/
        threadMode = ThreadMode.BackgroundThread;
    } else if (modifierString.equals("Async")) {
        /*onEventBusAsync:不管在哪個執行緒釋出事件,都會另起一個執行緒去處理事件。*/
        threadMode = ThreadMode.Async;
    } else {
        /*private final Map<Class<?>, Class<?>> skipMethodVerificationForClasses;*/
        /*判斷是不是可以跳過訂閱者方法校驗,不跳過就派出異常,跳過就返回null*/
        if (!skipMethodVerificationForClasses.containsKey(clazz)) {
            throw new EventBusException("Illegal onEvent method, check for typos: " + method);
        } else {
            threadMode = null;
        }
    }
    return threadMode;
}

相關文章