EventBus 使用及源碼分析

EventBus 是我們?nèi)粘i_發(fā)中使用很頻繁的一個三方庫,原因很簡單,使用 EventBus 可以很容實現(xiàn)解耦,不需要設(shè)置很多回調(diào)接口,如在 Fragment 和 Activity 之間的通訊,不同 Activity 之間、不同的線程之間,不同的 Service 之間等,使用 EventBus 會減少很多代碼,而且看起來更加簡潔,在 Android Studio 中點擊左側(cè)的小安卓圖標可以很方便從事件發(fā)送的位置跳轉(zhuǎn)到事件接收的位置,或者從事件接收的位置跳轉(zhuǎn)到事件的發(fā)送位置,如果你還沒試過這個小操作,那你需要了解一下了。

1. EventBus 使用

(1)添加依賴

implementation 'org.greenrobot:eventbus:3.1.1'

(2)創(chuàng)建事件的實體類

對于每個事件,一般我們用不同的實體類來進行區(qū)分,在實體類中我們還可以設(shè)置相關(guān)的字段,來進行參數(shù)的傳遞。

public class MessageEvent {
    
}

(3)訂閱者完成注冊和事件接收

事件的訂閱者,需要完成這兩個步驟,注冊和編寫事件接受的方法。如果沒有注冊,則接收不到事件,如果沒有設(shè)置接收事件方法,程序會報錯,開始使用 EventBus 的童鞋應(yīng)該會遇到這個問題,后面分析源碼時,我們再看為什么會報錯?

如果使用訂閱者有生命周期,還需要在銷毀前取消注冊,如 Activity。

注冊和取消注冊:

@Override
 public void onStart() {
     super.onStart();
     EventBus.getDefault().register(this);
 }

 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }

事件接收

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {
    // TODO 
};

(4)發(fā)送事件

EventBus.getDefault().post(new MessageEvent());

EventBus 使用起來就是這么簡單,代碼簡潔,而且能夠解耦。實際上 EventBus 的核心就是 訂閱者 - 發(fā)布模型。這里做一個比喻:可以想像一下,你經(jīng)常去報刊亭買你喜歡的科技雜志,但是你不知道這個雜志什么時候可以買,你想在報刊亭有這種雜志時就能夠及時買到。然后你就留下電話給報刊亭老板,新一期的科技雜志到了,老板就給你打電話通知你來買,這就是一個很簡單的發(fā)布訂閱模型。再有有很多像你一樣的報刊愛好者,都留下了電話,但是老板記不住怎么辦?所以需要有記錄一下,哪個人訂閱了哪種雜志,需要對應(yīng)起來,有可能一個雜志只有一個人訂閱,也有可能一個雜志有多個人訂閱。

eventbus.png

下面我們就來按照上面的訂閱者模型來分析一下源碼,EventBus 的源碼不多,可以就我們使用過程為出發(fā)點,分析 EventBus 是如何創(chuàng)建,注冊、發(fā)送、接收以及取消注冊的。

2. EventBus 源碼分析

EventBus 中類的關(guān)系圖

eventbus_class.png

2.1 構(gòu)建

EventBus.getDefault() 會返回一個 EventBus 實例,我們知道對于一個應(yīng)用程序,使用訂閱發(fā)布模型,一般要由一個實例來完成所有事件的管理和執(zhí)行,所以 EventBus 應(yīng)該是單例模式,EventBus 采用雙重檢驗鎖的單例模式。

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

當(dāng) EventBus 第一次初始化時,采用 Builder 模式來來構(gòu)建實例,主要初始化多個集合,訂閱者的各個執(zhí)行方法,訂閱個,訂閱者事件類型,需要分門別類的放到各個集合中。


EventBus(EventBusBuilder builder) {
    logger = builder.getLogger();
    // 訂閱者事件大集合,根據(jù)事件類型分類的 Map 集合
    subscriptionsByEventType = new HashMap<>();
    // 按訂閱者對象分類的集合,便于后面取消訂閱
    typesBySubscriber = new HashMap<>();
    // 粘性事件集合
    stickyEvents = new ConcurrentHashMap<>();
    mainThreadSupport = builder.getMainThreadSupport();
    // 3 種 Poster,實現(xiàn)主線程處理,后臺處理,異步處理
    mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
    backgroundPoster = new BackgroundPoster(this);
    asyncPoster = new AsyncPoster(this);
    indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
    // 訂閱者執(zhí)行函數(shù)存儲和查找的輔助類
    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;
    // 異步任務(wù)執(zhí)行器
    executorService = builder.executorService;
}

2.2 注冊

有了 EventBus 實例,就可以進行注冊。代碼很清晰,首先通過注冊的對象,獲取該對象的 Class,然后根據(jù)這個 Class 找到這個類中的訂閱事件,這里訂閱事件封裝到 SubscriberMethod 中,然后遍歷事件集合,將對象事件對應(yīng)起來注冊,實際上就是保存在一個集合中。

舉個小例子,如在 XxActivity 中注冊了,那么 EventBus 就會查找 XxActivity.class 文件中所有打上 @Subscribe 注解的方法。

這個過程我們可以很容易的想到,應(yīng)該通過反射的方式來獲取接收事件的函數(shù),源碼中是通過 SubscriberMethodFinder 這個訂閱者執(zhí)行函數(shù)存儲和查找的輔助類來完成的。

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

SubscriberMethodFinder 的 findSubscriberMethods 方法來查找注冊對象中所有的事件接收函數(shù),為了提高效率,源碼中做了緩存。


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

上面代碼中給出了注釋,如果一個訂閱者進行了注冊,但是沒有事件接收的方法,那么就會報錯,這也就回答了上面提到的如果沒有設(shè)置接收事件方法,程序為什么會報錯。

ignoreGeneratedIndex 這個表示是用來表明通過哪種方式來獲取注冊對象的所有事件接收函數(shù),默認情況下是通過反射來獲取。在3.0版本中,EventBus提供了一個EventBusAnnotationProcessor注解處理器來在編譯期通過讀取@Subscribe()注解并解析,處理其中所包含的信息,然后生成java類來保存所有訂閱者關(guān)于訂閱的信息,這樣就比在運行時使用反射來獲得這些訂閱者的信息速度要快。

這里我們只分析 findUsingReflection(subscriberClass) 方法,通過反射來獲取注冊對象的所有事件接收函數(shù)。

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findUsingReflectionInSingleClass(findState);
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

這里又使用了一個輔助類 FindState,我們只看主要代碼,遍歷訂閱者對象 subscriberClass以及它的父類們,并將遍歷的結(jié)果保存到 findState 中。

    private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // 獲取這個類的所有公有方法,包括父類的方法
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        // 遍歷所有方法,找到帶有 @Subscribe 注解的方法
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        // 注意這個事件類型,代表的是接收函數(shù)的類的類型
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                            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() 方法通過遍歷注冊對象的類型,通過反射獲取所有的公有的方法,然后根據(jù)注解找到事件接收函數(shù)的方法,然后將改方法進行封裝,封裝到 SubscriberMethod 中,最后添加到 findState 的 List<SubscriberMethod> subscriberMethods 集合中。

有幾點需注意下:

(1)通過反射 getDeclaredMethods 獲取所有的公有的方法,所以在編寫事件接收函數(shù)時,一定是 public 的,再有需要打上 @Subscribe 注解,這個不用多說

(2) Class<?> eventType = parameterTypes[0]; 這個參數(shù),代碼中給了注釋,這里再強調(diào)一下,事件接收函數(shù)一般我們只設(shè)定一個類型, EventBus 中也是根據(jù)這個類的類型來做區(qū)分的,所以這個 eventType 代表的就是我自定義類的類型,后面很多地方都會用到這個類型,不同的接收事件的區(qū)分就是根據(jù)這個類型來區(qū)分的。

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {
    // TODO 
};

找到了訂閱者的執(zhí)行函數(shù),那么就可以將訂閱者和訂閱函數(shù)封裝起來,將這個對象放到一個集合當(dāng)中,當(dāng)事件通知的時候,就從這個集合中取出來,執(zhí)行相應(yīng)的函數(shù)。下面來看注冊過程,驗證我們的想法。

public void register(Object subscriber) {
    // 查找注冊的訂閱者的事件接收函數(shù)
    Class<?> subscriberClass = subscriber.getClass();
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    // 開始執(zhí)行注冊過程
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

將訂閱者和訂閱方法對應(yīng)起來進行注冊,這個方法需要同步執(zhí)行,需要保證不能重復(fù)注冊,一但重復(fù)注冊,在接受事件時,訂閱者的執(zhí)行函數(shù)會被執(zhí)行兩次。

    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        // 1 根據(jù)事件類型將封裝的訂閱對象 newSubscription 放到大集合中,大集合是一個 Map 類型
        // key 是事件類型,value 是 CopyOnWriteArrayList 類型的訂閱事件的集合
        // 因為要保證同步,CopyOnWriteArrayList 是線程同步的
        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);
            }
        }
        // 2 同一類型的訂閱時間放在同一個集合中,即 subscriptions 中,放入的過程中,按照時間的優(yōu)先級插入到集合中,這樣執(zhí)行時,就按照優(yōu)先級來執(zhí)行。
        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;
            }
        }

        // 3 對于每個訂閱者還需要維護一個集合,每個訂閱者的事件類型,為什么需要有這樣一個集合?我們知道,一個事件發(fā)送時,可以得到事件的類型,根據(jù)事件類型通知所有的訂閱該類型的訂閱。在取消注冊時,需要根據(jù)訂閱者對象拿到訂閱的事件類型,然后再從大集合中將該對象的訂閱事件移除。
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);
        // 如果是粘性事件,立即執(zhí)行該事件,或者添加到執(zhí)行隊列當(dāng)中
        if (subscriberMethod.sticky) {
            if (eventInheritance) {
                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);
            }
        }
    }

到這里就將注冊的過程分析完了,總結(jié)一下:注冊時,先獲取 EventBus 的單例,然后通過輔助類 查找該訂閱者中的接收事件函數(shù)集合,然后將訂閱者和接執(zhí)行函數(shù)封裝到一個對象中,遍歷訂事件集合完成注冊,注冊的過程也就是將事件保存起來,等待事件過來時,拿到事件執(zhí)行函數(shù)開始執(zhí)行。

eventbus_register.png

2.3 事件發(fā)送過程分析

事件的發(fā)送過程的觸發(fā),也是通過一句代碼:

EventBus.getDefault().post(new MessageEvent());

下面就從 post 方法開始分析。

public void post(Object event) {
    // 1 獲取當(dāng)前線程的 Post 狀態(tài)
    PostingThreadState postingState = currentPostingThreadState.get();
    // 2 將事件加入到事件隊列中
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);
    // 3 通知訂閱該事件類型的訂閱者執(zhí)行事件函數(shù)
    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;
        }
    }
}

post() 事件發(fā)送后,執(zhí)行 3 個步驟:

(1) 獲取當(dāng)前線程的 Post 狀態(tài)

怎么保證是當(dāng)前發(fā)送者線程呢? currentPostingThreadState 是一個 ThreadLocal 變量,不同的線程擁有不同的 PostingThreadState 對象,所以 currentPostingThreadState.get() 獲取的就是發(fā)送者所處線程的 PostingThreadState 對象。

private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };

(2) 將事件加入到事件隊列中

這里有點標書不準確,object 是我們攜帶的參數(shù),并不是上面注冊過程中的事件,但是通過 object 可以查找出來,因為 object.getClass() 能夠得到事件的類型。

(3)通知訂閱該事件類型的訂閱者執(zhí)行事件函數(shù)

通過這句開始執(zhí)行 postSingleEvent(eventQueue.remove(0), postingState);

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    // 1.event 允許繼承的情況
    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 {
        // 2.event 不允許繼承的情況
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
    // 有一個訂閱事件執(zhí)行,subscriptionFound 就不為 false,這里是指一個訂閱事件都沒有執(zhí)行的情況
    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 允許繼承的情況的情況做了哪些,會遍歷 event 實現(xiàn)的接口以及繼承的父類,添加到集合中。這么說可能有點抽象。舉個栗子:如果允許繼承,假設(shè) 上面介紹 EventBus 中使用的自定義類 MessageEvent 繼承 BaseMessageEvent,那么 BaseMessageEvent.class 也會添加到 eventTypes 集合中,通過后面 MessageEvent.class 和 BaseMessageEvent.class 這兩個事件類型的訂閱事件都會被觸發(fā),這就是允許繼承的情況。

如果不允許繼承,那就只觸發(fā) MessageEvent.class 類型的訂閱事件。

private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
    synchronized (eventTypesCache) {
        List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
        if (eventTypes == null) {
            eventTypes = new ArrayList<>();
            Class<?> clazz = eventClass;
            while (clazz != null) {
                eventTypes.add(clazz);
                addInterfaces(eventTypes, clazz.getInterfaces());
                clazz = clazz.getSuperclass();
            }
            eventTypesCache.put(eventClass, eventTypes);
        }
        return eventTypes;
    }
}

接著看 postSingleEventForEventType(event, postingState, eventClass) 方法

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    // 1. 根據(jù)事件類型獲取訂閱事件
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    // 2. 遍歷執(zhí)行訂閱事件
    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 方法也很清晰,繼續(xù)看 postToSubscription(subscription, event, postingState.isMainThread);

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(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);
        }
    }

通過不同的 threadMode 在不同的線程里 invoke() 訂閱者的方法,ThreadMode 共有五類:

PostThread:默認的 ThreadMode,表示在執(zhí)行 Post 操作的線程直接調(diào)用訂閱者的事件響應(yīng)方法,不論該線程是否為主線程(UI 線程)。當(dāng)該線程為主線程時,響應(yīng)方法中不能有耗時操作,否則有卡主線程的風(fēng)險。適用場景:對于是否在主線程執(zhí)行無要求,但若 Post 線程為主線程,不能耗時的操作;

MainThread:在主線程中執(zhí)行響應(yīng)方法。如果發(fā)布線程就是主線程,則直接調(diào)用訂閱者的事件響應(yīng)方法,否則通過主線程的 Handler 發(fā)送消息在主線程中處理——調(diào)用訂閱者的事件響應(yīng)函數(shù)。顯然,MainThread類的方法也不能有耗時操作,以避免卡主線程。適用場景:必須在主線程執(zhí)行的操作;

MAIN_ORDERED:和 MainThread 類似,只是在執(zhí)行過程中,事件按照在隊列中的順序依次執(zhí)行。

BackgroundThread:在后臺線程中執(zhí)行響應(yīng)方法。如果發(fā)布線程不是主線程,則直接調(diào)用訂閱者的事件響應(yīng)函數(shù),否則啟動唯一的后臺線程去處理。由于后臺線程是唯一的,當(dāng)事件超過一個的時候,它們會被放在隊列中依次執(zhí)行,因此該類響應(yīng)方法雖然沒有PostThread類和MainThread類方法對性能敏感,但最好不要有重度耗時的操作或太頻繁的輕度耗時操作,以造成其他操作等待。適用場景:操作輕微耗時且不會過于頻繁,即一般的耗時操作都可以放在這里;

Async:不論發(fā)布線程是否為主線程,都使用一個空閑線程來處理。和BackgroundThread不同的是,Async類的所有線程是相互獨立的,因此不會出現(xiàn)卡線程的問題,Async 模式下采用的是線程池來執(zhí)行。適用場景:長耗時操作,例如網(wǎng)絡(luò)訪問。

在 postToSubscription 函數(shù)中執(zhí)行時,如果模式是主線程模式,則調(diào)用 invokeSubscriber(subscription, event); 直接執(zhí)行,如果不是主線程,需要通過 Poster 類加入隊列中,然后激活隊列依次執(zhí)行。這里以 mainThreadPoster 為例來分析一下。

mainThreadPoster 的實際類型是 HandlerPoster,繼承自 Handler,并實現(xiàn) Poster 接口,

public class HandlerPoster extends Handler implements Poster {

    private final PendingPostQueue queue;
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    private boolean handlerActive;

    protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
        super(looper);
        this.eventBus = eventBus;
        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
        queue = new PendingPostQueue();
    }

    // 1. 將消息加入到訂閱事件隊列中
    // 發(fā)送空消息,激活消息隊列
    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");
                }
            }
        }
    }

    // 2. 這部分很熟悉,消息回調(diào) Handler 的執(zhí)行方法,即執(zhí)行訂閱事件
    @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;
        }
    }
}

HandlerPoster 的作用就是將訂閱事件加入到消息隊列中,并激活消息隊列,開始執(zhí)行消息隊列中的訂閱事件,執(zhí)行過程是是通過反射執(zhí)行的,即 eventBus.invokeSubscriber(pendingPost);

源碼這里有一個巧妙的設(shè)計,消息隊列中的對象是一個 PendingPost 對象,其內(nèi)部維護一個對象池,主要完成復(fù)用的過程,防止創(chuàng)建過多的對象,具體就不分析了,自己看下源碼吧!

到這里,事件發(fā)送以及執(zhí)行的過程都分析完畢,總結(jié)起來:就是通過事件類型, 查找具該類型的所有訂閱事件(Subscription),然后遍歷執(zhí)行,執(zhí)行過程中,根據(jù)不同的模式來執(zhí)行,執(zhí)行過程采用的是反射方式。

event_bus_post.png

2.4 取消注冊

取消注冊過程就相對比較簡單了,實際上就是將訂閱事件從集合中移除,關(guān)鍵找到對應(yīng)的訂閱事件。還記得上面的 typesBySubscriber 集合嗎?它記錄了訂閱者中的訂閱事件的類型,通過訂閱者得到它的訂閱類型集合,然后根據(jù)這個類型找到這個訂閱者的訂閱事件(Subscription),然將它從大集合 subscriptionsByEventType 中把它移除。

public synchronized void unregister(Object subscriber) {
    // 1. 獲取事件類型
    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());
    }
}

private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
    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);
            if (subscription.subscriber == subscriber) {
                subscription.active = false;
                subscriptions.remove(i);
                i--;
                size--;
            }
        }
    }
}

3. 總結(jié)

EventBus 采用的是 訂閱-發(fā)布模型,基本思想:注冊時將訂閱者的類型以及執(zhí)行的函數(shù)放到集合中,發(fā)布事件時,根據(jù)事件類型遍歷集合,獲取相應(yīng)的訂閱事件,通知訂閱者,通過反射的方式執(zhí)行訂閱函數(shù)。

參考

EventBus

EventBus 3.0 源碼分析(流程圖來源于該文章-感謝)

EventBus源碼研讀

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • EventBus用法及源碼解析目錄介紹1.EventBus簡介1.1 EventBus的三要素1.2 EventB...
    楊充211閱讀 2,044評論 0 4
  • EventBus 是一款在 Android 開發(fā)中使用的發(fā)布/訂閱事件總線框架,基于觀察者模式,將事件的接收者和發(fā)...
    MonkeyLqj閱讀 10,559評論 3 31
  • EventBus是在Android中使用到的發(fā)布-訂閱事件總線框架,基于觀察者模式,將事件的發(fā)送者和接收者解耦,簡...
    BrotherTree閱讀 464評論 0 1
  • 最近在項目中使用了EventBus(3.0),覺得非常好用,于是就看了一些關(guān)于EventBus源碼分析的文章,現(xiàn)在...
    shenhuniurou閱讀 1,589評論 0 4
  • EventBus源碼分析 Android開發(fā)中我們最常用到的可以說就是EventBus了,今天我們來深入研究一下E...
    BlackFlag閱讀 546評論 3 4

友情鏈接更多精彩內(nèi)容