EventBus注册与发送源码剖析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ddxxii/article/details/81106726

EventBus使用咱们都会,而且使用起来很方便。那么来一起了解下我们平时使用的流程是怎么来的吧。

一:获取实例

咱们可以发现平时getDefault方法,其实它是使用了单例模式来做处理,如下。

  • getDefault
/**
     * Convenience singleton for apps using a process-wide EventBus instance.
     */
    public static EventBus getDefault() {
        EventBus instance = defaultInstance;
        if (instance == null) {
            synchronized (EventBus.class) {
                instance = EventBus.defaultInstance;
                if (instance == null) {
                    instance = EventBus.defaultInstance = new EventBus();
                }
            }
        }
        return instance;
    }

但是我们平时写单例是将构造方法写为private,它这里使用的是public的,也就是说,也可以使用new的方式来创建,默认使用了一个:

/**
     * Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
     * central bus, consider {@link #getDefault()}.
     */
    public EventBus() {
        //平时使用getDefault的时候实际上内部也是使用了构建则模式来创建
        this(DEFAULT_BUILDER);
    }

二. 注册与发送

先说说几个比较重要的数据结构,在这EventBus里面是起很关键作用:

 /**
     * @desc key为订阅事件类,该事件实现的所有接口为value
     */
    private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();

    /**
     * @description 订阅事件类作为key,订阅封装Subscription列表作为value的map
     * 后面要分发时间的时候,就可以通过事件类找到所有要分发的地方了
     */
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    /**
     * @description 以订阅对象为key,订阅事件列表为value的map
     * 一个类可以订阅多个事件,在这里就能获取到一个类订阅的所有事件了
     */
    private final Map<Object, List<Class<?>>> typesBySubscriber;
    /**
     * @description 粘性事件列表, 事件class为key,事件对象为value
     */
    private final Map<Class<?>, Object> stickyEvents;

    /**
     * @desc 当前线程发送状态,通过这个来控制以队列方式发送,取消等
     */
    private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };

    final static class PostingThreadState {
        /**
         * 队列,用于存储事件
         */
        final List<Object> eventQueue = new ArrayList<>();
        /**
         * 是否发送中
         */
        boolean isPosting;
        /**
         * 是否主线成
         */
        boolean isMainThread;
        /**
         * 订阅体
         */
        Subscription subscription;
        /**
         * 订阅事件
         */
        Object event;
        /**
         * 是否取消
         */
        boolean canceled;
    }

    /**
    * 订阅对象和订阅方法组成的订阅体
    */
    final class Subscription {
    /**
     * 订阅者对象
     */
    final Object subscriber;
    /**
     * 订阅方法
     */
    final SubscriberMethod subscriberMethod;
    }

    /**
    *订阅方法详情
    */
    public class SubscriberMethod {
    /**
     * 方法
     */
    final Method method;
    /**
     * 订阅的接收线程方式
     */
    final ThreadMode threadMode;
    /**
     * 订阅事件的class
     */
    final Class<?> eventType;
    /**
     * 优先级
     */
    final int priority;
    /**
     * 是否粘性
     */
    final boolean sticky;
    /** Used for efficient comparison */
    String methodString;
    }

注册开始

那么开始将注册了,通常以这种方式注册

if (!EventBus.getDefault().isRegistered(this)) {
        //没订阅再订阅
            EventBus.getDefault().register(this);
        }
  1. 检查是否订阅 isRegistered
public synchronized boolean isRegistered(Object subscriber) {   
        //返回订阅列表中查看是否已经存在了
        return typesBySubscriber.containsKey(subscriber);
    }
  1. 正式开始register
public void register(Object subscriber) {
        Log.d(TAG, "开始订阅");
        Class<?> subscriberClass = subscriber.getClass();
        //获取该类下订阅的方法封装类列表
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            //遍历订阅方法,进行订阅,将订阅的类和方法绑定起来
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }
  • subscriberMethodFinder.findSubscriberMethods
/**
     * 查找订阅方法
     *
     * @param subscriberClass
     * @return
     */
    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        Log.d(TAG, "寻找订阅方法");
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            Log.d(TAG, "从缓存中获取到订阅方法:" + subscriberMethods);
            return subscriberMethods;
        }

        Log.d(TAG, "findSubscriberMethods,ignoreGeneratedIndex:" + ignoreGeneratedIndex);
        //默认是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;
        }
    }
  • findUsingInfo
/**
     * 查找订阅方法
     *
     * @param subscriberClass
     * @return
     */
    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);
            Log.d(TAG, "findUsingInfo,findState.subscriberInfo:" + findState.subscriberInfo);
            if (findState.subscriberInfo != null) {
            } else {
                //默认返回是null,所以默认是走这里
                findUsingReflectionInSingleClass(findState);
            }
             //继续寻找父类中订阅的方法
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }
  • findUsingReflectionInSingleClass
/**
     * 利用反射寻找
     *
     * @param findState
     */
    private void findUsingReflectionInSingleClass(FindState findState) {
        Log.i(TAG, "findUsingReflectionInSingleClass ,strictMethodVerification:" + strictMethodVerification);
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            //获取实例的方法
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        //遍历实例方法来,获取到订阅的并添加到订阅方法列表中
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            //需要是publish方法和不是静态、抽象,BRIDGE | SYNTHETIC这两个没咋懂
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                //只有一个参数的方法,就找看有没有Subscribe注解
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        Log.d(TAG, "找到订阅方法");
                        //获取到订阅的参数类型
                        Class<?> eventType = parameterTypes[0];
                        //进行参数类型校验
                        if (findState.checkAdd(method, eventType)) {
                            //获取线程类型
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            //订阅方法列表添加进这个记录的方法(方法、方法参数、发送线程、优先级、是否粘性)
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    //如果打开了严格检查,那么方法又使用了Subscribe注解,就会抛异常(默认是关闭)
                    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                    throw new EventBusException("@Subscribe method " + methodName +
                            "must have exactly 1 parameter but has " + parameterTypes.length);
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                //如果打开了严格检查,那么方法又使用了Subscribe注解,就会抛异常(默认是关闭)
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }
        }
    }

这里就是通过反射来获取到对象中订阅了的方法事件,封装成SubscriberMethod,然后放在findState.subscriberMethods中

  • getMethodsAndRelease
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
        List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
        return subscriberMethods;
        }

其实就是返回刚刚装好的SubscriberMethod的list。返回找到了订阅的方法列表了,那么就开始循环遍历做订阅了。

  • subscribe
/**
     * @param subscriber
     * @param subscriberMethod
     * @desc 订阅处理, 将类订阅和方法绑定起来
     */
    // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        //获取方法的参数
        Class<?> eventType = subscriberMethod.eventType;
        //根据订阅类和方法创建一个Subscription对象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //从全局参数订阅列表中获取到所有的Subscription
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        //如果列表不存在则进行创建和绑定
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        }

        /*插入位置,根据优先级来遍历*/
        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<>();
            //添加到以订阅类为key,事件为value的列表中
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        //添加当前监听事件到列表里
        subscribedEvents.add(eventType);

        //如果方法订阅的是粘性方法那么还需要找找之前是否是有已经发送的粘性广播来进行消费
        if (subscriberMethod.sticky) {
            //默认是true
            if (eventInheritance) {
                Log.d(TAG, "遇到了粘性事件");
                // 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) {
                    //key为订阅者Class
                    Class<?> candidateEventType = entry.getKey();
                    Log.d(TAG, "注册粘性事件:" + candidateEventType);
                    //isAssignableFrom的意思是前者与后者相同或是前者是后者超类或接口,这里就是意思是订阅的事件类为订阅者的超类或接口
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Log.d(TAG, "注册粘性事件2:" + eventType);
                        Object stickyEvent = entry.getValue();
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } 
        }
    }

这段代码贴得比较多,它在订阅得时候分别添加到了两个Map中,一个是订阅事件为key,订阅封装Subscription列表作为value的map:subscriptionsByEventType,可以方便在分发时间的时候,就可以通过事件类找到所有要分发的地方了。另一个是订阅对象为key,订阅事件列表为value:typesBySubscriber,就可以方便获取到一个类订阅的所有事件。挺佩服得,这里对map用的很好。注册流程基本就结束了

发送开始

接下来是发送,也有两种,发送粘性得和普通的事件:

//普通,如果没有订阅者消费则丢弃
        EventBus.getDefault().post("11111");
        //粘性,如果没有订阅者消费则会存在换存里,后面订阅者订阅上后会进行消费
        EventBus.getDefault().postSticky("11111");
  • 普通的:
public void post(Object event) {
        //当前线程的发送状态类
        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;
            }
        }
    }
  • postSingleEvent
/**
     * @param event        要发送的事件
     * @param postingState 当前线程发送状态对象
     * @throws Error
     * @desc 单条发送
     */
    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        //要发送事件对象类
        Class<?> eventClass = event.getClass();
        //定义程没找到订阅者
        boolean subscriptionFound = false;
        //默认为true
        if (eventInheritance) {
            //获取到该事件自身和其所有接口
            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);
            }
        }
        //没找到分发
        if (!subscriptionFound) {
            //输出日志
            if (logNoSubscriberMessages) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            //以内部定义的没有注册的事件来发出去,所以也可以在某个地方订阅下NoSubscriberEvent来收集那些乱发事件的哦
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

准备开始分发,先找到事件及其接口,再来一条一条的遍历分发出去

  • postSingleEventForEventType
    /**
     * 根据事件类型发送事件
     *
     * @param event
     * @param postingState
     * @param eventClass
     * @return
     */
    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        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;
    }

这里根据事件类型,来分发给对应的订阅方

  • lookupAllEventTypes
/**
     * Looks up all Class objects including super classes and interfaces. Should also work for interfaces.
     *
     * @desc 获取订阅事件的对象列表
     */
    private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
        synchronized (eventTypesCache) {
            //用订阅事件类来获取该事件的所有接口
            List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
            if (eventTypes == null) {
                eventTypes = new ArrayList<>();
                Class<?> clazz = eventClass;
                while (clazz != null) {
                    //又将该事件类添加到了列表中
                    eventTypes.add(clazz);
                    //将该类实现的所有接口都添加到列表中
                    addInterfaces(eventTypes, clazz.getInterfaces());
                    //定位超类,一直遍历循环,直到到Object为止
                    clazz = clazz.getSuperclass();
                    if (clazz != null) {
                        Log.d(TAG, "lookupAllEventTypes:" + clazz.getName());
                    }
                }
                eventTypesCache.put(eventClass, eventTypes);
            }
            return eventTypes;
        }
    }

目的在于可以将事件的接口都给注意到,如果是以接口来做订阅的话,也能分发到它。这里可以想想里式替换原则就懂了

  • postToSubscription
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        Log.d(TAG, "postToSubscription:" + subscription.subscriberMethod.threadMode);
        switch (subscription.subscriberMethod.threadMode) {
            //在发送线程,直接调用
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            //在主线程直接调用,否则入队列
            case MAIN:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            //不阻塞主线成方式来发送,队列不为null则入队列,否则直接执行
            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) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            //异步分发,则是入一个队列,然后通过cache线程池,来进行执行订阅函数
            case ASYNC:
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

实际进行执行订阅的地方,根据订阅线程方式来决定该怎么执行订阅函数。
至此,普通的发送就结束啦,整体来说都还是很清晰的。

  • 粘性分发:
public void postSticky(Object event) {
        synchronized (stickyEvents) {
            //先放到粘性事件map中
            stickyEvents.put(event.getClass(), event);
        }
        // Should be posted after it is putted, in case the subscriber wants to remove immediately
        //普通事件发送方式进行发送
        post(event);
    }
s

结果粘性事件的发送是将事件最新的一次给存在了map中(因为是会覆盖的哈),然后进行了普通事件发送方式进行发送。发送完并没有看到从粘性事件map中移除(证实了,如果另外个地方再次注册,还是会收到上次已经消费的事件),所以建议在收到粘性事件后,手动进行移除一下,例如s

@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
    public void onRegistEvent2(TestEvent2 testEvent) {
        Log.i(TAG, "收到粘性事件啦:" + testEvent);
        //移除
        EventBus.getDefault().removeStickyEvent(testEvent);
    }

好了,发送也基本完了。可以愉快玩耍了,Eventbus的源码还是挺多的,这里只提到了注册和发送,就能看出其中对Map的使用得非常好,其中也用到不少设计模式,单例、构建者,看看源码后对思想还是有一种新迸发,如何使用也更好选择了

猜你喜欢

转载自blog.csdn.net/ddxxii/article/details/81106726