EventBus使用及源码分析

微笑、不失礼 提交于 2020-02-27 13:21:31

基于EventBus 3.1.1
主要参考:EventBus 3.0 源码分析
在其基础上添加了些个人理解,建议直接看参考原文

注册订阅者

EventBus.getDefault().register(this)

  1. 获取默认的 EventBus 实例

    在程序中创建多个EventBus 的实例,每个EventBus可看做一个管道,管道间相互独立

    • getDefault() 就是获取一个全局默认的管道, 因为多数场景下只需要用一个 EventBus 实例即可(尤其事件的收发需要同一个实例,所以提供了默认的实例的获取方法)
    • EventBusBuilder 设置的是 EventBus 的配置信息,即EventBus可能有几个属性在不同的应用场景下需要有不同的值;同时EventBus也有都是固定值的属性,这些直接在EventBus初始化时赋值或初始化即可
    • EventBus 的几个成员变量
      a. subscriptionsByEventType : Map<Class<?>, CopyOnWriteArrayList>
      b. typesBySubscriber : Map<Object, List<Class<?>>>
      c. stickyEvents: Map<Class<?>, Object>
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }
    
    public EventBus() {
    	// private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
        this(DEFAULT_BUILDER);
    }
    // 通过一个 EventBusBuilder 将配置解耦出去, 这些配置可能 不同的 EventBus 实例 需要有不同的配置
    // 而对于每个EventBus 都需要有的相同的属性 在 EventBus 初始化时 自身设置初值
    EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();
    	
    	// private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    	// 当前 EventBus 内 以事件类型为键值, 以订阅了该事件的 所有订阅者信息集合为值 的 map
        subscriptionsByEventType = new HashMap<>();
        // private final Map<Object, List<Class<?>>> typesBySubscriber;
        // 以订阅者对象为键,以该订阅者订阅了的事件类型的集合为值,用于 在unregister()被调用时,将该订阅者从 EventBus 的订阅者中移除
        typesBySubscriber = new HashMap<>();
        // private final Map<Class<?>, Object> stickyEvents;
        // 以事件类为键,以该事件类的一个对象为 值的 map
        // 用于存储每个订阅事件类的被发出的最新的一个粘性事件对象
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadSupport = builder.getMainThreadSupport();
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
    
    	// 负责 查找 订阅者的响应方法
        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;
        executorService = builder.executorService;
    }
    
  2. 注册订阅者

    注册订阅者,就是注册订阅者的订阅方法, 当有一个事件发布到当前管道中时(即 EventBus # post()发布了个事件),最终需要的是 订阅者的订阅方法们被调用,所以 是注册的订阅方法,注册订阅方法的同时也需要注册该方法的调用者即订阅者本身

    1. 注册订阅者,首先找到这个订阅者的所有订阅方法(将来在对应的订阅事件发出时,做出响应)
      a. 根据订阅者对象获取订阅者的类对象
      b. List<SubscriberMethod> subscribeMethodFinder.findSubscribeMethods() 找到这个订阅者的所有的 订阅方法信息;
      c. 依次注册当前订阅者的 所有订阅方法
    2. findSubscribeMethods(Class subscriberClass) 的内部实现:
      a. subscriberMethodFinder 实例在 EventBus 对象创建时被实例化,其生命周期与EventBus一致, 该订阅方法寻找类内部有一个 Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE,故它的生命周期也与EventBus一致,它作为一个备忘录,当寻找某个订阅者类的订阅方法时先在 map 中寻找,有直接返回,没有再找,找到后先存储在 map 中,再返回
      b. EventBus 3.0 提供一个注解处理器,在编译阶段通过读取解析 所有 订阅者类 中的 @Subscribe 注解信息,存储在一个 java类中(实际用 Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX 存储),等运行阶段就不用再通过反射获取订阅方法信息
      c. SubscriberInfo 是一个接口 声明了 订阅者订阅方法集合的获取方法
      d. ignoreGeneratedIndex 即为 EventBusBuilder 传入的配置信息,默认为false, 表示使用 b 中注解处理器解析的 订阅者的方法信息 而不是运行时通过反射获取订阅方法
      e. findUsingInfo() 通过 查找注解处理器生成的 map 获取 当前订阅者的 订阅方法集合
      f. findUsingReflection() 运行时通过反射获得 先跳过
    3. eventInheritance 可以父类引用指向子类对象, 即 发送了一个子类对象, 订阅方法订阅的是父类类型,那么也可以响应,实际收到的是子类对象,也是使用的子类对象的方法 或 属性
    public void register(Object subscriber) {
    	// 获取订阅者类的 类对象
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }
    
    // SubscribeMethod 的属性包括:
    // Method   方法对象
    // ThreadMode  通过 Subscribe 注解指定的属性  执行订阅方法的线程
    // eventType   响应的事件(接收的管道中的事件)
    // priority    响应方法执行的优先级 (一个事件发出后 统一管道下 优先级高的先执行 (是否为顺序??))
    // sticky      本方法是否响应粘性事件
    // methodString 
    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    	// private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
    	//  之前找过这个订阅者类的 订阅方法
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
    // EventBusBuilder 配置的属性, 是否由忽略EventBus提供的注解处理器 根据 订阅者类 的注解 在编译期生成的 订阅者类 (默认为false)
    // 生成的这个类
        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;
        }
    }
    
    /**
     * This class is generated by EventBus, do not edit.
     * 注解处理器 读取 所有订阅者的  注解信息 生成的 类
     */
    public class MyEventBusIndex implements SubscriberInfoIndex {
        private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
    
        static {
            SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
    
            putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.PerfTestEventBus.SubscriberClassEventBusAsync.class,
            		// 
                    true, new SubscriberMethodInfo[]{
                    // 实例包括: methodName, threadMode, priority, sticky, eventType
                    new SubscriberMethodInfo("onEventAsync", TestEvent.class, ThreadMode.ASYNC),
            }));
    		// SimpleSubscriberInfo(Class subscriberClass, boolean shouldCheckSuperclass, SubscriberMethodInfo[] methodInfos)
    		//                        订阅者类类对象                  是否要检查父类            订阅者的订阅方法集合               
            putIndex(new SimpleSubscriberInfo(TestRunnerActivity.class, true, new SubscriberMethodInfo[]{
                    new SubscriberMethodInfo("onEventMainThread", TestFinishedEvent.class, ThreadMode.MAIN),
            }));
        }
    
        private static void putIndex(SubscriberInfo info) {
            SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
        }
    
        @Override
        public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
            SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
            if (info != null) {
                return info;
            } else {
                return null;
            }
        }
    }
    

    方法注册过程

    // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    	// 当前方法订阅事件的类型
        Class<?> eventType = subscriberMethod.eventType;
        // 包裹订阅者  和  当前订阅方法
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //  根据类型找 这个EventBus管道中 订阅了当前类型的 订阅包裹(包订阅者和订阅方法)的集合,没有就新建,有就取出来 看当前订阅者是不是已经注册了  是就抛异常
        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);
            }
        }
    	// 将当前包裹  放到 当前 EventType 对应的 包裹集合中    根据  优先级放入合适的位置
        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;
            }
        }
    	// 将此方法的响应事件   放到    当前订阅者   订阅的 所有事件的集合中
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);
    
        if (subscriberMethod.sticky) { // 当前方法响应粘性事件
        	// 通过 EventBusBuilder 配置的  默认为true
        	// 即 当前响应 A 类对象  现在收到了它的子类对象   那么  也响应
        	// 其实就是 父类引用指向子类对象, 实际传入的是子类对象, 多态的体现
            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();
                    // 父类.class.isAssignableFrom(子类.class)
                    // 即 eventType  是   candidateEventType  的父类
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                //  传入根据 类型获取的   粘性事件 对象
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }
    
    private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
            // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
            // --> Strange corner case, which we don't take care of here.
            //  后续是 实际的 处理过程
            postToSubscription(newSubscription, stickyEvent, isMainThread());
        }
    }
    
    

    在这里插入图片描述

发送事件

EventBus.getDefault().post(new SecondToFirstEvent("2发送给1的事件"));

将事件发送到 EventBus 这个管子中

  1. post(Object event) 每个线程中有一个 PostingThreadState 记录当前线程 事件的投递状态
    a. 这个 PostingThreadState 内部靠一个 ArrayList 存储要发到 EventBus 中的事件
    b. 每次投递都把 ArrayList 中的事件都投递出去
    c. post() 中 事件先被加入到这个 ArrayList中
  2. postSingleEvent(Object event, PostingThreadState postingState)
    a. 判断是否能找到该事件的订阅者, 不能找到 看是否需要发 NoSubscriberEvent
  3. postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass)
    a. 根据类型 在 subscriptionsByEventType 中 找到订阅类型为本事件的 所有订阅者包裹(订阅者及方法)
  4. postToSubscription(subscription, event, postingState.isMainThread)
    a. 真正的执行
    /** Posts the given event to the event bus. */
    public void post(Object event) {
    	// ThreadLocal<PostingThreadState> currentPostingThreadState
    	// 线程内部存储的  postingState
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

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

    final static class PostingThreadState {
        final List<Object> eventQueue = new ArrayList<>();
        boolean isPosting;
        boolean isMainThread;
        Subscription subscription;
        Object event;
        boolean canceled;
    }

    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false; //  是否找到了当前事件的响应方法
        if (eventInheritance) { // 是否让接收 当前事件父类的 订阅方法响应本事件
        	// 获取 eventClass 的所有父类以及接口
            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 {
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        if (!subscriptionFound) {  //  没有订阅者响应本事件
            if (logNoSubscriberMessages) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            // EventBusBuilder配置的 
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event)); //  发送一个表示 没有订阅者响应的事件 
            }
        }
    }

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

执行事件

根据订阅方法的定义的 线程模式 选用不同的执行线程

  • POSTING
    响应方法执行线程与发布事件的线程一致,若执行线程为主线程 则不能进行耗时的操作
  • MAIN
  • BACKGROUND 适用于轻微耗时,且不会操作不会过于频繁 (相当于只有一个线程在执行这类事件)
  • ASYNC 适用于长耗时操作 如 网络访问, 有一个事件 就线程池中取一个线程执行它
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 {
            	// HandlerPoster 继承自Handler,它在主线程中创建, 此方法调用时,将 订阅信息 和 事件入队,并给这个 Handler 发送一个消息
            	// 接着 该 Handler 的 handleMessage() 就会被调用, 在其中 eventBus.invokeSubscriber()
                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) {
            	// 实现自Runnable, 在run() 中将队列中的 逐个 包裹取出,逐个执行
            	// 本方法执行时,将 包裹 入队, 用 ExecutorService 执行这个 Runnable
            	// 队列中的包裹 即 响应本事件的订阅方法们  是 按照顺序依次执行的, 一个执行完另一个才能执行,
                backgroundPoster.enqueue(subscription, event);
            } else {
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
        	// 也是实现 Runnable 接口, 调用本方法 先包裹入队,然后用线程池执行本 Runnable,  run() 方法中 会取出刚刚入队的包裹  执行 eventBus.invokeSubscriber()
        	//  分发一个响应的订阅事件就起一个线程执行它     是异步的 并发的
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}
// 直接通过反射调用
void invokeSubscriber(Subscription subscription, Object event) {
    try { //  找到方法对象  传入 真正的调用者 及 参数 到 invoke() 中
        subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
    } catch (InvocationTargetException e) {
        handleSubscriberException(subscription, event, e.getCause());
    } catch (IllegalAccessException e) {
        throw new IllegalStateException("Unexpected exception", e);
    }
}

在这里插入图片描述

解除注册

EventBus.getDefault().unregister(this);

  1. 找到该订阅者订阅的所有的事件类型
  2. 根据找到事件类型 找到 该事件类型的所有的订阅者, 从这所有的订阅者中 将当前订阅者移除
/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
    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());
    }
}

/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
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--;
            }
        }
    }
}

引用:

业内对EventBus的主要争论点是在于EventBus使用反射会出现性能问题,关于反射的性能问题可以参考这篇文章,
实际上在EventBus里我们可以看到不仅可以使用注解处理器预处理获取订阅信息,EventBus也会将订阅者的方法缓存到METHOD_CACHE里避免重复查找,所以只有在最后
invoke()方法的时候会比直接调用多出一些性能损耗,但是这些对于我们移动端来说是完全可以忽略的.所以盲目的说因为性能问题而觉得EventBus不值得使用显然是不
负责任的

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!