EventBus源码分析​(一):构造及订阅

Quibbler 2021-9-15 699

EventBus源码分析(一):构造及订阅


        通过一文EventBus初步使用,了解了EventBus的使用。光了解是不够的,深入源码才算熟悉。EventBus源码不多,适合开发者精读学习,仅代码就1800余行(使用SourceCounter统计)




1、构造EventBus实例

        项目开发中,一般使用EventBus类中的getDefault()静态方法获取全局总线实例。

    EventBus eventBus = EventBus.getDefault();


1.1、默认单例

        getDefault()是标准的获取单例方法,获取EventBus库中实现的静态单例。关于单例详见单例的五种方式一文。

    static volatile EventBus defaultInstance;

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


1.2、构造方法

        用来构造EventBus的构造方法是这个无参构造方法,通过该默认构造方法也可以构造出一个EventBus实例,不过不推荐,应使用官方提供的getDefault()方法。

    public EventBus() {
        this(DEFAULT_BUILDER);
    }

        默认构造方法中使用默认的EventBusBuilder构造器构造EventBus实例。注意到这个构造方法默认包访问权限。


        开发者也就不能直接通过EventBus构造函数设置EventBusBuilder,并作为参数构造EventBus。有没有方法自定义设置EventBus呢?有的,继续看下面1.3节。

    EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        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;
    }


1.3、建造者 EventBusBuilder

        EventBusBuilder也不是公开的构造方法。开发者不能直接通过构造方法创建EventBusBuilder实例。

    EventBusBuilder() {
    }

        EventBus类中有默认的EventBusBuilder构造器DEFAULT_BUILDER用来创建默认的EventBus实例。

    private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

        但是,在EventBus类中却提供的静态方法可以创建并获取EventBusBuilder实例:

    public static EventBusBuilder builder() {
        return new EventBusBuilder();
    }

        通过建造者模式配置各种EventBus特性配置。建造者模式也是很多开源库里经常用到的设计模式之一,比如OkHttp中Request请求的构造就是通过建造者来配置构造请求。

    private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
    boolean logSubscriberExceptions = true;
    boolean logNoSubscriberMessages = true;
    boolean sendSubscriberExceptionEvent = true;
    boolean sendNoSubscriberEvent = true;
    boolean throwSubscriberException;
    boolean eventInheritance = true;
    boolean ignoreGeneratedIndex;
    boolean strictMethodVerification;
    ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
    List<Class<?>> skipMethodVerificationForClasses;
    List<SubscriberInfoIndex> subscriberInfoIndexes;
    Logger logger;
    MainThreadSupport mainThreadSupport;

        配置好EventBusBuilder建造者,那么EventBusBuilder如何使用建造者呢?毕竟EventBus的有参构造方法是私有的,还好EventBusBuilder建造者提供了一个installDefaultEventBus()方法,初始化默认的EventBus单例,注意不是“替换”!如果EventBus已有单例存在,那么调用该方法会报错。

    public EventBus installDefaultEventBus() {
        synchronized (EventBus.class) {
            if (EventBus.defaultInstance != null) {
                throw new EventBusException("Default instance already exists." +
                        " It may be only set once before it's used the first time to ensure consistent behavior.");
            }
            EventBus.defaultInstance = build();
            return EventBus.defaultInstance;
        }
    }

        或者直接通过EventBusBuilder建造者的build()方法创建一个新的EventBus实例:

    /** Builds an EventBus based on the current configuration. */
    public EventBus build() {
        return new EventBus(this);
    }


1.4、小结

          了EventBus的构造源码,可以知道获取EventBus实例的四种方法:

          EventBus.getDefault()    //getDefault()获取默认的全局总线

          new EventBus()             //EventBus的两个构造方法只有这个默认的可以直接使用

          EventBus.builder().installDefaultEventBus()            //只能设置一次,而且必须先于getDefault()设置

          EventBus.builder().build()                                       //使用EventBusBuilder建造自定义EventBus实例

          通过构造方法创建的实例还需要开发者自己维护,开发者只需要使用getDefault()获取唯一的总线实例就行了。



2、注册订阅者

        EventBus事件总线已经拿到了,接下来就该注册订阅者。EventBus库内部通过以下几个映射维护订阅者和事件的关系。

    //维护 [事件类型] 到 [订阅者]的映射。某个[事件类型],对应一个或者多个[订阅者]。
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    
    //维护[订阅者] 到 [订阅事件类型]的映射。一个[订阅者]可以订阅一个或多个[事件类型]。
    private final Map<Object, List<Class<?>>> typesBySubscriber;
    
    //记录[事件类型] 到 对应[粘性事件]的映射 。注册粘性订阅者时,会将[粘性事件]再次发送给订阅该[类型事件]的[订阅者]。
    private final Map<Class<?>, Object> stickyEvents;

        在开始阅读源码之前,最好先要知道这一点,方便后面代码的理解。


2.1、注册订阅者

        EventBus的注册流程比解注册要稍微复杂一些,因为涉及到注册对象注解的解析,订阅者、事件类型相互之间的关系映射。整体流程如下:


        从register(Object subscriber)方法开始看:

    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

        借助SubscriberMethodFinder类,解析订阅对象中的用@Subscribe注解的订阅方法,调用findSubscriberMethods(Class<?> subscriberClass)方法:

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        //默认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;
        }
    }

        构造EventBus时使用默认的EventBusBuilderignoreGeneratedIndex默认配置为false,所以执行findUsingInfo(Class<?> subscriberClass)方法:

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        //借助FindState来遍历查找注解,这里用到了对象池,从对象池中获取FindState实例
        FindState findState = prepareFindState();
        //初始化FindState ,findState.clazz初始为null
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) {
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                //所以走这里的路基
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        //从FindState中拿出找到的List<SubscriberMethod>,并释放FindState到资源池。
        return getMethodsAndRelease(findState);
    }


        值得学习的是,为了提高性能,这里用到了对象池以复用FindState实例。对象池可以模仿小巧的对象池Pools一文的实现。

    private static final int POOL_SIZE = 4;
    private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];

        在findUsingInfo(Class<?> subscriberClass)方法开始调用prepareFindState()获取对象池中的对象,如果没有可用的实例就直接创建新的:

    private FindState prepareFindState() {
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                FindState state = FIND_STATE_POOL[i];
                if (state != null) {
                    FIND_STATE_POOL[i] = null;
                    return state;
                }
            }
        }
        return new FindState();
    }

        最后再通过getMethodsAndRelease(FindState findState)方法返回订阅对象的注解方法解析结果,并且释放对象回到对象池(如果没满)

    private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
        List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
        findState.recycle();
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                if (FIND_STATE_POOL[i] == null) {
                    FIND_STATE_POOL[i] = findState;
                    break;
                }
            }
        }
        return subscriberMethods;
    }


        接着findUsingInfo(Class<?> subscriberClass)方法继续看,内部调用findUsingReflectionInSingleClass(FindState findState)方法真正完成了注解解析操作

    private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            ...
        }
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    //添加解析出的订阅方法到FindState中
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    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)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }
        }
    }

        从上面的方法可以看到几个开发注意事项,这也是EventBus的硬性规定。相信很多开发者都曾经遇到过:
        注解的订阅方法必须有且仅有一个参数,多了或者少了都会抛出异常提示:must have exactly 1 parameter...

        注解的方法必须是public,否则抛出异常提示:@Subscribe method: must be public...

        解析完后,回到findSubscriberMethods(Class<?> subscriberClass)方法,会发现还有一处限定:

        注册的对象中没有找到注解的订阅方法,则抛出:have no public methods with the @Subscribe annotation...


        绕了一大圈,再回到最初的register(Object subscriber)方法,通过SubscriberMethodFinder找到注册对象中的所有订阅方法。

    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                //执行订阅
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

        现在对它们进行订阅,执行subscribe(Object subscriber, SubscriberMethod subscriberMethod)方法,看着比较多,且一行一行分析:

    // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        //事件类型,也就是注册方法的参数类型
        Class<?> eventType = subscriberMethod.eventType;
        
        //用订阅者和其中的订阅方法构造一个订阅对象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        
        //从已注册的事件映射中找一找,是不是已经有对象订阅了该事件
        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);
            }
        }
        
        //按照订阅方法设置的优先级,将订阅对象插入到这个事件的订阅对象队列中。
        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) {
            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);
            }
        }
    }

          看到这里,订阅对象已经注册到事件中线中了。本篇暂不对事件的分发展开探究。


2.2、解注册订阅者

        解注册一个从来没被注册的对象并不会抛出异常,反而重复注册一个对象会抛出重复注册的异常。通过isRegistered(Object subscriber)方法判断一个对象是否被注册到EventBus中,其实就是判断在不在typesBySubscriber映射中:

    public synchronized boolean isRegistered(Object subscriber) {
        return typesBySubscriber.containsKey(subscriber);
    }

        订阅者的解注册,就是将订阅对象及对应的订阅事件类型从typesBySubscriber中删除。

    /** 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映射中删除
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

        再根据事件类型从subscriptionsByEventType拿到这个事件的所有订阅者,并从中删除当前需要取消订阅的对象:

    /** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
    private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        //从subscriptionsByEventType映射中获取所有这种事件类型的订阅者
        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 3.0 源码分析


不忘初心的阿甘
最新回复 (0)
    • 安卓笔记本
      2
        登录 注册 QQ
返回
仅供学习交流,切勿用于商业用途。如有错误欢迎指出:fluent0418@gmail.com