三幅图弄懂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方法,也就是调用注册的方法执行。
在这里插入图片描述

EventBus线程模式

EventBus支持监听方法在不同线程中进行回调,可以通过ThreadMode来进行设置用法如下:

@Subscribe(threadMode = ThreadMode.MainThread)
    public void  onNewsEvent(NewsEvent event){
        String message = event.getMessage();
        mTv_message.setText(message);
    }

ThreadMode总共有四种方式:
1、PostThread

事件的处理在和事件的发送在相同的进程,所以事件处理时间不应太长,不然影响事件的发送线程。

2、MainThread

事件的处理会在UI线程中执行。事件处理时间不能太长,这个不用说的,长了会ANR的。

3、BackgroundThread

如果事件是在UI线程中发布出来的,那么事件处理就会在子线程中运行,如果事件本来就是子线程中发布出来的,那么事件处理直接在该子线程中执行。所有待处理事件会被加到一个队列中,由对应线程依次处理这些事件,如果某个事件处理时间太长,会阻塞后面的事件的派发或处理。

4、Async

事件处理会在单独的线程中执行,主要用于在后台线程中执行耗时操作,每个事件会开启一个线程。

总结

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、需要自己注册和反注册,如果忘了反注册就会导致内存泄漏

©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页