三幅圖弄懂EventBus核心原理

前言

好多分析EventBus的文章,喜歡上來就貼源碼,我看了好屢次老是迷迷糊糊的,此次花時間完全整理一下EventBus,發現EventBus核心其實就是三幅圖,這三幅圖涉及的是三個HashMap表,弄懂這三幅圖那麼EventBus就懂了。java

一、第一幅圖(訂閱者和訂閱事件)

先看一段在activity中註冊和反註冊EventBus的代碼。ide

onStart{
	   EventBus.getDefault().register(this);
	}

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent1(Event1 event) {
    }
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent2(Event2 event) {
     }
	
	onStop{
	   EventBus.getDefault().register(this);
	}

複製代碼

看上面的代碼,註冊監聽的是activity,稱爲subscriber,在activity中監聽了Event1和Event2兩個事件,如今在另外一個位置執行了一段代碼:post

EventBus.getDefault().post(new Event1());
複製代碼

這個時候,activity中的onEvent1就會收到事件。下面引入第一幅圖: 性能

在這裏插入圖片描述
如圖所示,一個Subscribe對應多個Event,Subsribe就是上面經過register方法註冊的對象,好比activity。這幅圖對應EventBus中一個Map結構:

private final Map<Object, List<Class<?>>> typesBySubscriber;
複製代碼

EventBus會在對象register時,使用反射機制,遍歷對象的方法,將帶有@Subscribe標籤而且合法的方法加入到typesBySubscriber。typesBySubscriber是HashMap形式,key是註冊的對象自己,因爲一個註冊的對象中可能有多個監聽事件,因此value是用list保存的Event。this

看下register方法中如何處理的spa

public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }
    // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
    }

複製代碼

上面的代碼主要作兩件事:一、經過反射遍歷註冊對象的方法,獲取其中帶有@Subscribe標籤的方法而且放在一個列表中,最後以註冊對象爲key,@Subscribe的方法列表做爲value放在HashMap中,就是上圖的形式。code

思考cdn

一、爲何要將註冊監聽對象做爲key,監聽事件列表做爲value放在HashMap中? 要弄懂一個問題,EventBus是觀察者模式,上面的activity也就是subscribe是訂閱者,activity中的event是訂閱事件,一個訂閱者能夠訂閱多個事件,移除一個訂閱者的監聽事件時,應該將其中全部的event的事件移除。 也就是說在反註冊的時候,會經過Subsribe來查找到其中全部額event進行反註冊。對象

二、第二幅圖(訂閱事件和訂閱者)

在這裏插入圖片描述
這種表關係是event和subsciption的對應關係,好比在Android中多個activity可能會註冊監聽同一個event事件,因此在執行:

EventBus.getDefault().post(new Event1());
複製代碼

的時候全部註冊監聽了Event1的監聽都會要會收到回調,看下subsciption的結構blog

在這裏插入圖片描述
subsciption中包含,訂閱的事件和訂閱者自己,上面中全部的event就是訂閱的事件,在Android中訂閱的事件代碼以下:

@Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(Event event) {
    }
複製代碼

而subsriber就是訂閱者好比會在activity的onstart中執行

....
EventBus.getDefault().register(this);
複製代碼

那麼subsribe就是activity。

思考 爲何須要保存Event和subsribe對應的關係表?

這是由於一個Event可能會有被多個subsribe訂閱,因此有當執行post(Event)的時候會查找到全部訂閱了Event事件的subscribe並調用其中的event方法。下面看下post方法:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        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;
    }
    }
複製代碼

post和postSticky主要都會調用到上面的方法,上面方法中subscriptionsByEventType.get(eventClass)就是經過event的類型找上面的表中找到對應的subscriptions進行通知的。

第三幅圖

在看第三幅圖以前思考一個問題,postSticky究竟是怎麼執行的?爲何先執行postSticky,後執行register仍是能夠監聽到event事件? 先看postSticky代碼:

public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        // Should be posted after it is putted, in case the subscriber wants to remove immediately
        post(event);
    }
複製代碼

原來執行postSticky的時候會將event.getclass和event保存起來,而後再看下subscribe代碼:

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);
            
複製代碼

先判斷註冊監聽的event是否是sticky的若是是就會用stickEvents表中找到stickyEvent若是若是註冊的事件event和stickyEvent同樣那麼就會執行一次postToSubscription方法,也就是調用註冊的方法執行。

在這裏插入圖片描述

總結

一、要理解EventBus就要從register,unRegister,post,postSticky方法入手。要理解register實質上是將訂閱對象(好比activity)中的每一個帶有subscriber的方法找出來,最後得到調用的就是這些方法。訂閱對象(好比activity)是一組event方法的持有者。

二、後註冊的對象中sticky方法可以收到以前的stickyEvent方法的緣由是EventBus中維護了stickyEvent的hashMap表,在subsribe註冊的時候就遍歷其中有沒有註冊監聽stickyEvent若是有就會執行一次回調。

EventBus缺點

一、使用的時候有定義不少event類, 二、event在註冊的時候會調用反射去遍歷註冊對象的方法在其中找出帶有@subscriber標籤的方法,性能不高。 三、須要本身註冊和反註冊,若是忘了反註冊就會致使內存泄漏

相關文章
相關標籤/搜索