三幅圖弄懂EventBus核心原理

前言

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

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

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

	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兩個事件,現在在另一個位置執行了一段代碼:

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。

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

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

思考

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

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

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

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

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

在這裏插入圖片描述
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方法,也就是調用註冊的方法執行。
在這裏插入圖片描述

總結

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

2、後註冊的對象中sticky方法能夠收到之前的stickyEvent方法的原因是EventBus中維護了stickyEvent的hashMap表,在subsribe註冊的時候就遍歷其中有沒有註冊監聽stickyEvent如果有就會執行一次回調。

EventBus缺點

1、使用的時候有定義很多event類,
2、event在註冊的時候會調用反射去遍歷註冊對象的方法在其中找出帶有@subscriber標籤的方法,性能不高。
3、需要自己註冊和反註冊,如果忘了反註冊就會導致內存泄漏

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章