學(xué)習(xí)EventBus之訂閱

相信做Android開發(fā)的很少有沒聽說過EventBus的,如果真沒聽過那只能說明你Out了。EventBus是針對(duì)Android平臺(tái)優(yōu)化了的發(fā)布/訂閱事件總線,它的目的是簡化Android組件以及線程間的通信,從而使代碼更加簡潔。

官方給的流程圖

簡單使用

  • 定義事件類型
    public class MessageEvent{}
  • 訂閱者訂閱事件
    eventBus.register(this)
  • 發(fā)送事件
    eventBus.post(messageEvent)

通過以上三步,就完成一次完整的EventBus使用流程。

其實(shí)類似這種發(fā)布/訂閱的機(jī)制在生活中用的也挺多的。比如我晚上定個(gè)8點(diǎn)的鬧鐘,就相當(dāng)于訂閱了一個(gè)事件,這個(gè)事件就是'8點(diǎn)鐘的鬧鐘'。當(dāng)?shù)诙?點(diǎn)鬧鐘響的時(shí)候,就相當(dāng)于發(fā)送了事件。我就會(huì)收到這個(gè)事件,然后執(zhí)行相應(yīng)的動(dòng)作,比如起床。

那么,既然是訂閱/發(fā)布。肯定就會(huì)有訂閱,有發(fā)布了。這篇文章先根據(jù)源碼來研究下訂閱事件的流程和原理。

一般我們都會(huì)像這樣EventBus.getDefault().register(this)使用EventBus。這里首先會(huì)創(chuàng)建一個(gè)默認(rèn)的EventBus對(duì)象,然后調(diào)用EventBus的register(Object subscriber)方法將當(dāng)前類作為訂閱者進(jìn)行事件的訂閱。

/** 
Convenience singleton for apps using a process-wide EventBus instance. 
*/
public static EventBus getDefault() {
      if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
}

可以看到,源碼是通過單例模式來維護(hù)一個(gè)EventBus的實(shí)例的。從注釋中看以看到,這個(gè)實(shí)例的作用于為整個(gè)進(jìn)程。

這里的關(guān)鍵點(diǎn)就是register(Object subscriber)方法了,上源碼:

public void register(Object subscriber) {
    register(subscriber, false, 0);
}

private synchronized void register(Object subscriber, boolean sticky, int priority) {
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
    for (SubscriberMethod subscriberMethod : subscriberMethods) {
        //遍歷所有訂閱方法進(jìn)行訂閱
        subscribe(subscriber, subscriberMethod, sticky, priority);
    }
}

可以看到register(Object subscriber)方法內(nèi)部調(diào)用了重載的方法register(Object subscriber, boolean sticky, int priority),所有的邏輯都在這個(gè)重載的方法內(nèi)部實(shí)現(xiàn)。

首先執(zhí)行subscriberMethodFinder.findSubscriberMethods(subscriber.getClass()),我們跟蹤到findSubscriberMethods(Class<?> subscriberClass)方法內(nèi)部去看看。

//尋找subscriberClass中所有的訂閱方法
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    String key = subscriberClass.getName();
    List<SubscriberMethod> subscriberMethods;
    synchronized (methodCache) {//先從緩存中根據(jù)類名找對(duì)應(yīng)的方法,如果沒找到說明這個(gè)類之前沒有注冊(cè)過
        subscriberMethods = methodCache.get(key);
    }
    if (subscriberMethods != null) {//如果找到了則直接返回,不用再查找了
        return subscriberMethods;
    }
    subscriberMethods = new ArrayList<SubscriberMethod>();
    Class<?> clazz = subscriberClass;
    HashMap<String, Class> eventTypesFound = new HashMap<String, Class>();
    StringBuilder methodKeyBuilder = new StringBuilder();
    while (clazz != null) {
        String name = clazz.getName();
        if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {//排除系統(tǒng)的一些類
    // Skip system classes, this just degrades performance
        break;
}

    // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
        try {
        // This is faster than getMethods, especially when subscribers a fat classes like Activities
                Method[] methods = clazz.getDeclaredMethods();//拿到訂閱者中聲明的所有方法
                filterSubscriberMethods(subscriberMethods, eventTypesFound, methodKeyBuilder, methods);//對(duì)方法進(jìn)行過濾,找到符合訂閱規(guī)則的訂閱方法
            } 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();
        }
        if (subscriberMethods.isEmpty()) {//查找完后如果沒找到,報(bào)異常:該類訂閱了EventBus,但沒有對(duì)應(yīng)的訂閱方法
            throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
                    + ON_EVENT_METHOD_NAME);
        } else {
            synchronized (methodCache) {//如果找到了,則存起來。防止重復(fù)查找。這里的key就是方法名
                methodCache.put(key, subscriberMethods);
            }
            return subscriberMethods;
        }
    }
//對(duì)方法進(jìn)行過濾,篩選出符合事件接收的方法 形如 "public void onEvent**(Object event)"
private void filterSubscriberMethods(List<SubscriberMethod> subscriberMethods,
                                         HashMap<String, Class> eventTypesFound, StringBuilder methodKeyBuilder,
                                         Method[] methods) {
    for (Method method : methods) {
        String methodName = method.getName();
        if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {//方法以onEvent開頭
            int modifiers = method.getModifiers(); //方法的限定符類型 public  private等
            Class<?> methodClass = method.getDeclaringClass();//聲明方法的類
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                //方法是public的 且非abstract static (bridge synthetic 這兩個(gè)啥意思?)
                Class<?>[] parameterTypes = method.getParameterTypes(); //方法參數(shù)類型
                if (parameterTypes.length == 1) {//方法只有一個(gè)參數(shù)
                    ThreadMode threadMode = getThreadMode(methodClass, method, methodName);
                    if (threadMode == null) {//不是訂閱方法,繼續(xù)迭代下一個(gè)方法
                        continue;
                    }
                    Class<?> eventType = parameterTypes[0];
                    methodKeyBuilder.setLength(0);
                    methodKeyBuilder.append(methodName);
                    methodKeyBuilder.append('>').append(eventType.getName());
                    String methodKey = methodKeyBuilder.toString();
                    Class methodClassOld = eventTypesFound.put(methodKey, methodClass);//以方法名和event類型名作為key
                                                                                        //存入訂閱方法列表中
                    if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
                        // Only add if not already found in a sub class
                        subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
                    } else {
                        // 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);
            }
        }
    }
}

//判斷訂閱方法需要在哪個(gè)線程中調(diào)用
private ThreadMode getThreadMode(Class<?> clazz, Method method, String methodName) {
    String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());//截取方法名中onEvent后的字符
                                                                                    //如 onEventMainThread -> MainThread
    ThreadMode threadMode;
    if (modifierString.length() == 0) { //方法名為onEVent 默認(rèn)在PostThread中調(diào)用
        threadMode = ThreadMode.PostThread;
    } else if (modifierString.equals("MainThread")) {//方法名為onEVentMainThread 在MainThread中調(diào)用
        threadMode = ThreadMode.MainThread;
    } else if (modifierString.equals("BackgroundThread")) {//方法名為onEVentBackgroundThread 在BackgroundThread中調(diào)用
        threadMode = ThreadMode.BackgroundThread;
    } else if (modifierString.equals("Async")) {//方法名為onEVentAsync 異步調(diào)用
        threadMode = ThreadMode.Async;
    } else {//如果不是以上幾個(gè)選擇 認(rèn)為不是訂閱方法 threadMode為null
        if (!skipMethodVerificationForClasses.containsKey(clazz)) {
                throw new EventBusException("Illegal onEvent method, check for typos: " + method);
        } else {
            threadMode = null;
        }
    }
    return threadMode;
}

上面代碼比較長,注釋的已經(jīng)很清楚了。findSubscriberMethods(Class<?> subscriberClass)執(zhí)行完之后,就找到了訂閱者類中所有的能夠接受事件的方法并存了起來。

我們繼續(xù)回到register(Object subscriber, boolean sticky, int priority)方法中,查詢完訂閱方法后,對(duì)所有的方法進(jìn)行迭代,然后調(diào)用subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)進(jìn)行一一注冊(cè)。

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
    Class<?> eventType = subscriberMethod.eventType;
    //根據(jù)事件類型取出該類事件的所有訂閱者
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
    if (subscriptions == null) {//如果沒有該類型的訂閱者 創(chuàng)建一個(gè) 加入列表
        subscriptions = new CopyOnWriteArrayList<Subscription>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        if (subscriptions.contains(newSubscription)) {//如果所有訂閱者中已經(jīng)包含新加入的這個(gè)訂閱者,提示已經(jīng)訂閱了
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                    + eventType);
        }
    }

    // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
    // subscriberMethod.method.setAccessible(true);

    int size = subscriptions.size();
    //遍歷所有的訂閱者 按照優(yōu)先級(jí)(Subscription的priority屬性)進(jìn)行排序
    for (int i = 0; i <= size; i++) {
        if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    //取出某個(gè)訂閱者中所有的訂閱事件
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<Class<?>>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

    if (sticky) {//這塊是對(duì)黏性事件的處理。目前項(xiàng)目中還沒用過,不太理解這塊
        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);
        }
    }
}

當(dāng)subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)方法執(zhí)行完之后,所有的訂閱者以及訂閱者中的事件接受方法就被存起來了。

//按事件類型存放的訂閱者集合
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
//按訂閱者類型存放的事件集合
private final Map<Object, List<Class<?>>> typesBySubscriber;
//黏性事件(這個(gè)怎么理解?)
private final Map<Class<?>, Object> stickyEvents;

至此,事件的訂閱就完成了。最終的結(jié)果就是EventBus保存了訂閱者中所有符合規(guī)則(即public void onEvent***(Object event))的能接收事件的方法。當(dāng)post事件的時(shí)候,就會(huì)拿著這個(gè)事件去保存的訂閱者方法中去對(duì)比尋找,找到后通過反射去調(diào)用對(duì)應(yīng)的方法。

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

推薦閱讀更多精彩內(nèi)容

  • 項(xiàng)目到了一定階段會(huì)出現(xiàn)一種甜蜜的負(fù)擔(dān):業(yè)務(wù)的不斷發(fā)展與人員的流動(dòng)性越來越大,代碼維護(hù)與測(cè)試回歸流程越來越繁瑣。這個(gè)...
    fdacc6a1e764閱讀 3,210評(píng)論 0 6
  • 原文鏈接:http://blog.csdn.net/u012810020/article/details/7005...
    tinyjoy閱讀 565評(píng)論 1 5
  • EventBus源碼分析(一) EventBus官方介紹為一個(gè)為Android系統(tǒng)優(yōu)化的事件訂閱總線,它不僅可以很...
    蕉下孤客閱讀 4,048評(píng)論 4 42
  • 先吐槽一下博客園的MarkDown編輯器,推出的時(shí)候還很高興博客園支持MarkDown了,試用了下發(fā)現(xiàn)支持不完善就...
    Ten_Minutes閱讀 582評(píng)論 0 2
  • Android 淺析 EventBus (二) 原理 前言 Linus Benedict Torvalds : R...
    CodePlayer_Jz閱讀 3,428評(píng)論 0 6