三幅图弄懂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标签的方法,性能不高。 三、须要本身注册和反注册,若是忘了反注册就会致使内存泄漏

相关文章
相关标签/搜索