EventBus源码分析(五)

作者: eirunye | 来源:发表于2018-10-25 16:34 被阅读18次

    简介

    前面我学习了如何使用EventBus,还有了解了EventBus的特性,那么接下来我们一起来学习EventBus的源码,查看EventBus的源码,看看EventBus给我们带来什么惊喜以及编程思想。

    EventBus-Android的发布 - 订阅

    这个图我们从一开始就一直放置在上面了。我们在来回顾一下,EventBus的官网是怎么定义它的呢?
    EventBus是Android和Java的发布/订阅(观察者模式)事件总线。
    我们大概了解了EventBus的构建思想。接下来我们进入源码学习吧。

    进入源码分析

    我们从EventBus的注册开始入手。

    EventBus.getDefault().register(this);
    
    public static EventBus getDefault() {
            if (defaultInstance == null) {
                synchronized (EventBus.class) {
                    if (defaultInstance == null) {
                        //创建了EventBus实例,进入下面的方法
                        defaultInstance = new EventBus(); 
                    }
                }
            }
            return defaultInstance;
        }
    

    上面的方法是一个双重校验的单例。

    public EventBus() {
            this(DEFAULT_BUILDER); //private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
        }
    

    DEFAULT_BUILDEREventBusBuilder.class实例来创建的,这个类非常重要:使用自定义参数创建EventBus实例,还允许自定义默认EventBus实例,如前面的例子使用索引、配置EventBus的配置&事件的优先级&使用索引(四)等就是通过这个类来实现的,大家可以回顾一下。进入初始化一下必要的参数的构造方法EventBus(EventBusBuilder builder),如下

    EventBus(EventBusBuilder builder) {
       logger = builder.getLogger(); /初始化Logger
       subscriptionsByEventType = new HashMap<>(); //存储订阅事件的
       typesBySubscriber = new HashMap<>(); //存储相关订阅者class列表,key是注册者的对象,value是订阅者的class列表
       stickyEvents = new ConcurrentHashMap<>();  //粘性事件
       //下面这4个实例就是我们设置方法``threadMode = ThreadMode.xxx``
       //*1
       mainThreadSupport = builder.getMainThreadSupport();
       mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
       backgroundPoster = new BackgroundPoster(this);
       asyncPoster = new AsyncPoster(this);
      //获取注册者信息索引(添加由EventBus的注释预处理器生成的索引)
       indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
      //初始订阅方法查找器。这个类主要具有查找订阅的方法,继续往下看
       subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
       builder.strictMethodVerification, builder.ignoreGeneratedIndex);
       logSubscriberExceptions = builder.logSubscriberExceptions; //是否有日记订阅,默认true
       logNoSubscriberMessages = builder.logNoSubscriberMessages;//是否有日记信息,默认 true
       sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;//是否发送订阅异常事件,默认true
       sendNoSubscriberEvent = builder.sendNoSubscriberEvent;  //是否发送没有订阅事件,默认true
       throwSubscriberException = builder.throwSubscriberException; //是否抛出异常处理
      //默认情况下,EventBus会考虑事件类层次结构(将通知超类的注册者)。 关闭此功能将改善事件的发布。对于直接扩展Object的简单事件类,我们测量事件发布的速度提高了20%。
       eventInheritance = builder.eventInheritance; 
       executorService = builder.executorService;    //创建线程池
     }    
    

    我们将此段代码逐步分析.
    这步主要是进行初始化话一下必要的参数,如代码注解所示。
    下面这段代码就是我们常用的@Subscribe(threadMode = ThreadMode.xxx);初始化。一般常用的就是以下4种。

    mainThreadSupport = builder.getMainThreadSupport();
    mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
    backgroundPoster = new BackgroundPoster(this); 
    asyncPoster = new AsyncPoster(this);
    

    我们来看看builder.getMainThreadSupport()方法返回的是MainThreadSupport接口,表示为支持Android主线程。
    上面的4个线程中都持有 PendingPostQueue等待发送的队列实例。
    mainThreadSupport.createPoster(this)创建一个HandlerPoster而该类继承了Handle,并且初始化了一个等待发布队列。代码如下。

    public interface MainThreadSupport {
        boolean isMainThread();
        Poster createPoster(EventBus eventBus);
        class AndroidHandlerMainThreadSupport implements MainThreadSupport {
            private final Looper looper;
            public AndroidHandlerMainThreadSupport(Looper looper) {
                this.looper = looper;
            }
            @Override
            public boolean isMainThread() {
                return looper == Looper.myLooper();
            }
            @Override
            public Poster createPoster(EventBus eventBus) {
                return new HandlerPoster(eventBus, looper, 10);
            }
        }
    }
    
    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                    builder.strictMethodVerification, builder.ignoreGeneratedIndex);
    

    上面代码在这里主要初始化订阅的方法查找器。下面会讲解到它是如何进行订阅方法的。
    我们这这里知道了EventBus初始化,然后相关的实例的创建,接下来我我们进入到register(this)方法的调用如下方法。

    public void register(Object subscriber) {
            //获取当前注册者对象的Class。
            Class<?> subscriberClass = subscriber.getClass(); 
            //通过该注册者的`Class`来获取当前注册者里面由`@Subscriber`注解绑定的方法。
            List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
            //必须在线程同步中进行。
            synchronized (this) {
                for (SubscriberMethod subscriberMethod : subscriberMethods) {
                    subscribe(subscriber, subscriberMethod);
                }
            }
        }
    

    上面的代码表示的是给注册者接收事件。 传递当前所注册的对象,如Activity、Fragment

    • 注意: 注册者如果不再接收信息,必须调用unregister(Object)方法,表示解除注册则该订阅者将不再接收到数据了,如果不进行解除将可能出现内存泄漏。一般在onDestroy方法解除注册后面会讲解到。 在注册者中拥有必须由@Subscribe注解的方法。@Subscribe还允许配置ThreadMode和优先级、是否是粘性行为。

    接着,我们进入List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);方法。

    //private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>(); //线程安全
    
    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
      //从Map集合中获取订阅方法列表
       List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
      //判断当前获取方法是否为空
      if (subscriberMethods != null) {
           return subscriberMethods;
      }
      //通过EventBusBuilder.ignoreGeneratedIndex
      //是否忽略配置索引,默认是忽略索引
      if (ignoreGeneratedIndex) {
        //1.通过反射获取方法列表
         subscriberMethods = findUsingReflection(subscriberClass);
       } else {
        //2.通过索引方式获取订阅方法列表
         subscriberMethods = findUsingInfo(subscriberClass);
       }
      //是否订阅方法为空,如果为空则表示该订阅者里面的方法都不符合EventBus订阅方法的规则
      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;
       }
    }
    

    我们逐步分析该段代码。
    该段代码通过注册者的类来获取当前dingy的方法列表,如果不为空则直接返回订阅方法。
    通过反射来查找订阅方法,如果该方法为空则抛出异常:该订阅者的方法和其超类没有@Subscriber注解的共有方法(即表示不符合EventBus订阅方法的规则)。如果不为空则存储到ConcurrentHashMap集合中。
    这里的是由ConcurrentHashMap集合存储是以当前订阅者的classkey,而将其由@Subscriber绑定的方法添加到List中,,就是集合的value。必须保持线程安全的,所以这里使用了ConcurrentHashMap

    • 1.是否忽略索引设置,该方法表示忽略设置索引,进入该方法findUsingReflection(Class<?> subscriberClass),通过反射进行获取List<SubscriberMethod>
    • 注:设置索引在EventBus的配置&事件的优先级&使用索引(四)这里说的,大家可以去看看。
    private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
            FindState findState = prepareFindState();//准备查找状态
            findState.initForSubscriber(subscriberClass);// 初始化订阅者信息
            while (findState.clazz != null) {//循环获取订阅者的class是否为空
                //通过反射获取订阅方法,
                // 3.该方法下面分析
                findUsingReflectionInSingleClass(findState); 
                findState.moveToSuperclass(); //删除超类class
            }
               ////获取订阅方法list
            return getMethodsAndRelease(findState);
        }
    

    上面的代码主要是通过反射来获取相关的订阅方法,里面由静态内部类FindState进行管理相关信息,由于上面的方法和下面索引的方法都将调用同一个方法,所以放在下面来将讲解,请看下面信息。

    • 2.是否忽略索引设置,该方法表示的设置了索引,进入findUsingInfo(Class<?> subscriberClass)方法,如下。
    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
            FindState findState = prepareFindState(); //准备查找状态
            findState.initForSubscriber(subscriberClass);// 初始化订阅者信息
            while (findState.clazz != null) { //循环获取订阅者的class是否为空
                findState.subscriberInfo = getSubscriberInfo(findState);//获取获取订阅者信息,这里如果使用添加索引的话,这里将能获取到索引的相关信息。
                if (findState.subscriberInfo != null) { 
                    //获取订阅方法
                    SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                    for (SubscriberMethod subscriberMethod : array) {
                     //进行等级检查,并存储到Map集合中,该方法checkAdd是FindState.class中的方法 ,key是@Subscriber中参数的class,value是该subscriberMethod.method,也就是@Subscribe中的方法如:public void onMessageEvent(MessageEvent event)
                     if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                            //将该订阅方法添加到列表中
                            findState.subscriberMethods.add(subscriberMethod);
                        }
                    }
                } else {
                    //3.通过使用反射的方式来获取,和上面忽略索引调用到同一个方法,这里将解析
                    findUsingReflectionInSingleClass(findState);
                }
                //清除父类的方法
                findState.moveToSuperclass();
            }
               //获取订阅方法list
            return getMethodsAndRelease(findState);
        }
    

    分析本段代码。
    首先进入查找的准备状态,通过默认状态池返回状态信息,主要存储的是FindState实例。主要存储的有订阅者的class、订阅者信息SubscriberInfo。订阅者@Subscribe方法列表List<SubscriberMethod>等.
    如果当前订阅者class不为空,则将获取到订阅者信息。这里分两种情况。
    如果使用添加索引的话,getSubscriberInfo(findState)该方法将获取到订阅信息,如果没有使用索引的话则将调用findUsingReflectionInSingleClass(findState);该方法来获取信息

    • 使用添加索引,并返回订阅方法,该方法如下
     //获取订阅方法
     SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
    

    由于AbstractSubscriberInfo类实现了SubscriberInfo接口,而SimpleSubscriberInfo继承了AbstractSubscriberInfo并实现了getSubscriberMethods方法,代码如下:
    本段代码在SimpleSubscriberInfo.class中。返回订阅方法数组SubscriberMethod[]

    @Override
        public synchronized SubscriberMethod[] getSubscriberMethods() {
            int length = methodInfos.length;
            SubscriberMethod[] methods = new SubscriberMethod[length];
            for (int i = 0; i < length; i++) {
                SubscriberMethodInfo info = methodInfos[i];
                methods[i] = createSubscriberMethod(info.methodName, info.eventType, info.threadMode,
                        info.priority, info.sticky);
            }
            return methods;
        }
    
    • 3.如果没有添加索引则进入该方法findUsingReflectionInSingleClass(findState);
      通过反射来获取订阅者方法。这个方法有点长,慢慢看。
    private void findUsingReflectionInSingleClass(FindState findState) {
            Method[] methods;
            try {
                // This is faster than getMethods, especially when subscribers are fat classes like Activities
                //该方法代替getMethods()方法,获取该订阅者class全部方法。
                methods = findState.clazz.getDeclaredMethods();
            } catch (Throwable th) {
                // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
                methods = findState.clazz.getMethods(); //获取该订阅者class全部方法
                findState.skipSuperClasses = true;  //设置跳过超类
            }
            for (Method method : methods) {
                int modifiers = method.getModifiers();//获取修饰符
                if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //通过与运算判断当前的修饰符,是否符合EventBus方法定义
                    Class<?>[] parameterTypes = method.getParameterTypes(); //获取参数类型
                    if (parameterTypes.length == 1) { //因为EventBus方法中必须有参数,所以当参数为1时,符合要求
                        //通过注解的形式@Subscribe获取Subscribe,默认的Subscribe为(priority==0,sticky=false,threadMode=POSTING),就是说ThreadMode为POSTING,粘性为false,优先级为0,如果方法中设置了相应的值,则将是你设置的值。如threadMode=MAIN。
                        Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                        if (subscribeAnnotation != null) {
                            Class<?> eventType = parameterTypes[0]; //获取当前参数的class
                            //进行等级检查,并存储到Map集合中,该方法checkAdd是FindState.class中的方法 ,key是@Subscriber中参数的class,value是该subscriberMethod.method,也就是@Subscribe中的方法如:public void onMessageEvent(MessageEvent event)
                            if (findState.checkAdd(method, eventType)) { 
                                // 获取ThreadMode ,如MAIN
                                ThreadMode threadMode = subscribeAnnotation.threadMode();
                                //将订阅方法添加到列表中
                                findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                        subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                            }
                        }
                    } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { //不符合EventBus订阅方法的规则要求。抛出异常,提示该方法必须是@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");
                }
            }
        }
    

    该段代码主要是获取该注册者的全部方法,并进行筛选出来符合EventBus的订阅方法的规则,通过注解的形式来获取订阅方法,最后添加到查找状态的列表中。
    首先获取的是修饰符,参数类型,再通过注解的形式来获取订阅方法。如下注解Subscribe,如果不符合EventBus相关订阅方法的规则将抛出异常提示,是否在方法上书写了错误的方法。
    接下来我们进入看看@Subscribe订阅方法是通过注解的形式来设置的。

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface Subscribe {
         //ThreadMode ,默认POSTING
        ThreadMode threadMode() default ThreadMode.POSTING;
        //粘性事件,默认false
        boolean sticky() default false;
        //优先级,默认0
        int priority() default 0;
    }
    

    接下来进入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; //将查找状态器findState,添加到状态池FIND_STATE_POOL中,为下次直接从查找状态池中获取
                        break;
                    }
                }
            }
            return subscriberMethods; // 返回订阅方法列表
        }
    

    上面该方法通过FindState静态内部类获取了订阅的列表,然后被存储到了ConcurrentHashMap集合中。并且存储到状态池中,为方便下次读取。并且回收资源。
    获取到List<SubscriberMethod>订阅列表后,再次回到了注册方法中register(Object subscriber)进入到方法subscribe(subscriber, subscriberMethod);该方法通过循环进行遍历

    • 注 此处必须是在线程同步中进行,所以添加synchronized。
    //这里的参数subscriber表示的是订阅者,如Activity等,subscriberMethod表示订阅方法,该方法就是在subscriber里面
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
            Class<?> eventType = subscriberMethod.eventType; //获取事件类型,也就是参数的class
            Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
            //通过事件类型参数class获取CopyOnWriteArrayList<Subscription>列表,该类大家可以研究一下,
            //是写时复制容器,即当我们往`CopyOnWriteArrayList`容器添加元素时,先从原有的数组中拷贝一份出来,然后在新的容器做写操作(添加元素),写完之后,再将原来的数组引用指向到新数组的形式存储数据的,它是线程安全的。
            CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
            if (subscriptions == null) {
                subscriptions = new CopyOnWriteArrayList<>();
                subscriptionsByEventType.put(eventType, subscriptions); //添加到Map集合中
            } 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);//优先级高的添加添加到CopyOnWriteArrayList容器最前面,只有一个的时候就是添加在第一位了。
                    break;
                }
            }
            //获取订阅事件的class列表,即订阅方法的参数class列表
            List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
            if (subscribedEvents == null) {
                subscribedEvents = new ArrayList<>();
                //存储到Map集合,key是订阅者(即上面传递的Activity对象),value是订阅事件列表
                typesBySubscriber.put(subscriber, subscribedEvents);
            }
            subscribedEvents.add(eventType); //将该事件类型class添加到订阅事件列表中
            判断当前方法是否是粘性的
            if (subscriberMethod.sticky) {
                if (eventInheritance) {
                    //必须考虑eventType的所有子类的现有粘性事件
                    // 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();
                          isAssignableFrom(Class<?> cls)方法表示的类型是否可以通过标识转换或通过扩展引用转换为此类对象表示的类型,则返回true
                        if (eventType.isAssignableFrom(candidateEventType)) {
                            Object stickyEvent = entry.getValue();
                            //将粘性事件添加到订阅者列表中
                            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                        }
                    }
                } else {
                    //将粘性事件添加到订阅者列表中
                    Object stickyEvent = stickyEvents.get(eventType);
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        }
    

    上面的方法中主要是通过获取"事件类型"即订阅方法中的参数class。然后将其添加到Map集合中,这里用到了CopyOnWriteArrayList(原理:是写时复制容器,即当我们往CopyOnWriteArrayList容器添加元素时,先从原有的数组中拷贝一份出来,然后在新的容器做写操作(添加元素),写完之后,再将原来的数组引用指向到新数组的形式存储数据的,它是线程安全的。)
    然后进行判断是否设置优先级,遍历并添加到subscriptions列表中。
    通过当前的注册者,获取注册者class列表,并添加到typesBySubscriber集合中,key是注册者的class,如:Activity,而value就是订阅方法中的参数class的列表。这个地方说明了,一个注册者,可以拥有多个订阅事件(方法),将其绑定起来,存储到Map集合中。最后将该参数class添加到subscribedEvents.add(eventType);列表中。
    下面方法是检查粘性事件订阅发送事件方法,如下

    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.
                //这段代码我们到post发送事件的调用的时候讲解。
                postToSubscription(newSubscription, stickyEvent, isMainThread());
            }
        }
    
    • 注:这里主要是检查是否发送粘性发送事件,方法 postToSubscription我们到发送事件POST方法调用时讲解。

    • 事件发送
      接下来我进入post方法,事件发送。此时,我们回忆一下简介中的图:
      post()==>EventBus==>将分发到各个订阅事件中。也就是订阅方法。我们来看看是如何进行操作的。代码如下

    EventBus.getDefault().post(new MessageEvent(message));
    

    通过以上代码我大概的知道,post()方法里面的参数是一个实体的对象,而该实体就是我们在订阅方法中的参数实体。你发现了什么了吗?
    前边我们已经分析了,该注册者的订阅方法主要的存储过程,接下来我们一起进入探究吧。方法跟踪进入post()方法。

    /** Posts the given event to the event bus. */
        //给订阅事件,发送事件总线
        public void post(Object event) {
            //获取当前线程,即通过ThreadLocal本地线程来管理对应的事件线程,并且初始化发送线程状态PostingThreadState
            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;
                }
            }
        }
    

    currentPostingThreadState.get()方法中我们知道

    private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
            @Override
            protected PostingThreadState initialValue() {
                return new PostingThreadState();
            }
        };
    

    通过ThreadLocal这里就叫"本地线程"吧,来管理当前的发送线程状态,每个发送线程将得到对应一个PostingThreadState,而该PostingThreadState管理eventQueue信息,该信息主要存储的是发送事件。
    通过eventQueue.add(event)存储该发送事件以后完成以后,就判断当前的发送事件状态是否是正在发送中,如果还没发送则当该eventQueue不为为空的时候,进入循环发送该事件,发送完一个就删除一个,直到发送完成为止。
    进入方法postSingleEvent(Object event, PostingThreadState postingState)中。

    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
            Class<?> eventClass = event.getClass();
            boolean subscriptionFound = false;
            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);
                }
            } else {
                subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
            }
            if (!subscriptionFound) {
                if (logNoSubscriberMessages) {
                    logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
                }
                if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                        eventClass != SubscriberExceptionEvent.class) {
                    post(new NoSubscriberEvent(this, event));
                }
            }
        }
    

    上面的方法首先获取该事件类型的class然后传递到lookupAllEventTypes(eventClass)方法中,通过该方法获取当前发送事件的class,包括父类、实现的接口等等列表。所以一般情况下会返回当前发送的事件,和Object.class(注:当然了,如果该事件实现接口的话,也会包括实现的接口的。)列表即List<Class<?>>,然后进行遍历进行或运算。
    我们先进入lookupAllEventTypes(eventClass)方法,看看返回的事件类型,并且知道他存储到Map集合中,即key是当前事件class,value是该事件的所有类型,包括父类,接口等。

    /** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */
        private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
            //private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>(); key是当前事件类型的class,valu则是事件的所有类型,包括父类,接口等。本事例会得到的是MessageEvent.class、和Object.class
            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());
                        clazz = clazz.getSuperclass(); //获取父类class
                    }
                    eventTypesCache.put(eventClass, eventTypes); //存储d
                }
                return eventTypes;
            }
        }
    

    看看方法postSingleEventForEventType(event, postingState, clazz),发送事件

    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
            CopyOnWriteArrayList<Subscription> subscriptions;
            synchronized (this) {
                 //通过当前的事件class,获取订阅事件列表
                subscriptions = subscriptionsByEventType.get(eventClass);
            }
            if (subscriptions != null && !subscriptions.isEmpty()) {
                for (Subscription subscription : subscriptions) {
                    //对PostingThreadState进行赋值
                    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;
        }
    

    大家是否还记得在注册的时候出现的CopyOnWriteArrayList<Subscription>在这里就用到了,通过发送事件的class获取CopyOnWriteArrayList容器里面的订阅事件信息,包括注册者对象,订阅方法等。然后遍历并进行发送事件。在此方法发布postToSubscription()事件

     /**
      * subscription:发布订阅的事件处理器
      * event:当前发布的事件
      * isMainThread:是否是在主线程中
      */
    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 {
                        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) {
                        backgroundPoster.enqueue(subscription, event);
                    } else {
                        invokeSubscriber(subscription, event);
                    }
                    break;
                case ASYNC:
                    asyncPoster.enqueue(subscription, event);
                    break;
                default:
                    throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
            }
        }
    

    好了,这就是之前我们在注册中有一个地方遗留下的,就是判断是否是粘性状态是调用的方法,大家往回看,就看到了在方法checkPostStickyEventToSubscription中有调用到该方法。
    这里会根据,在方法中所选的threadMode进行调用,

    • 1.MAIN,如果当前是否是UI线程,则会进入到invokeSubscriber(subscription, event);方法中。否则将调用mainThreadPoster.enqueue(subscription, event);这个方法将交给HandlerPoster.class里面的enqueue(Subscription subscription, Object event)方法,HandlerPoster.class现实了该接口,移交给handle完成,大家可以进入HandlerPoster.class看看,这里就不说了。
    • 2.POSTING直接调用invokeSubscriber(subscription, event);
    • 3.MAIN_ORDERED如果当前mainThreadPoster不为空则调用mainThreadPoster.enqueue(subscription, event);MAIN进入HandlerPoster.class,反之调用invokeSubscriber(subscription, event);
    • 4.BACKGROUND如果当前是主线程,则调用backgroundPoster.enqueue(subscription, event);,反之调用invokeSubscriber(subscription, event);
    • 5.ASYNC,异步方法调用,执行asyncPoster.enqueue(subscription, event);,方法进行跟踪,该类有实现了Runnable接口,然后调用eventBus.invokeSubscriber(pendingPost);,最终将调用了invoke方法。
    void invokeSubscriber(Subscription subscription, Object event) {
            try {
                subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
            } catch (InvocationTargetException e) {
                handleSubscriberException(subscription, event, e.getCause());
            } catch (IllegalAccessException e) {
                throw new IllegalStateException("Unexpected exception", e);
            }
        }
    

    invoke()这是一个本地的方法,将调用C/C++的到方法进行发布。最后将进入到订阅方法中。并传递该事件到到订阅的方法中。
    好了,这里我们将post发布事件的方法简单的分析了一下。整个EventBus的分析差不多完成了,但是,我们还有一点不能忘记就是解除绑定。接下来我们来看看解除绑定的方法。

    • 注:该方法一般生命周期的onDestroy()方法中进行解除绑定。
      代码如下:
    EventBus.getDefault().unregister(this);
    

    直接进入unregister(this)

    /** 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. */
        /**
         * subscriber:指代当前解除注册的对象如Activity,
         * eventType:发送的事件类型,post函数的实体对象class
         */
    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--;
                }
            }
       }
    }
    

    解除绑定,这里很简单。这里就不一一进行解释了,_

    总结

    1.本篇文章主要对EventBus源码进行简单的分析。主要是进行了观察者模式的一些高级运用。如果大家对观察者模式理解不怎么清楚可以进入这里看看简单的案例观察者模式,内容非常简单。
    2.相关的EvenBut的使用,请看之前的内容。如EventBus认识(一)EventBus的ThreadMode使用以及分析(二)等等。
    3.学习本篇文章中可以认识到一些常用的类如CopyOnWriteArrayListThreadLocal等,到时可以深入研究一下,有助我们提高。
    4.一些编程思想,设计模式值得我们去学习的,如单例模式EventBus双重校验、建造者模式,EventBus构造器的时候用到,用了初始化各个参数等等。面向接口编程而不针对实现编程。
    5.如果有什么问题希望大家进行指正,好好学习,一起进步。

    相关文章

      网友评论

        本文标题:EventBus源码分析(五)

        本文链接:https://www.haomeiwen.com/subject/pttkzftx.html