EventBus源码分析(二)——粘性事件

前言

在EventBus源码分析(一)中我们介绍了事件的注册、发送与反注册,本文的粘性事件是基于上篇文章,不同于一般事件,粘性事件主要在注册的时候有些不同的处理。

一、事件注册

粘性事件的注册方式是这样的:

@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void pdmEventMainThread(Student student) {
        Log.e(TAG,"onEventMainThread: " + student.getContent());
    }

主要是在订阅方法的注解里面加入了粘性标,设置sticky = true;
我们看下注解类:

public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;

    /**
     * If true, delivers the most recent sticky event (posted with
     * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
     */
    boolean sticky() default false;

    /** Subscriber priority to influence the order of event delivery.
     * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
     * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
     * delivery among subscribers with different {@link ThreadMode}s! */
    int priority() default 0;
}

总共三个参数

threadMode代表事件接收的线程,取值范围是POSTING 、MAIN 、BACKGROUND 、ASYNC

  • POSTING:事件的处理在和事件的发送在相同的线程,所以事件处理时间不应太长,不然影响事件的发送线程。
  • MAIN:事件的处理会在UI线程中执行,事件处理不应太长时间
  • BACKGROUND :事件的处理会在一个后台线程中执行,尽管是在后台线程中运行,事件处理时间不应太长。如果事件分发在主线程,件会被加到一个队列中,由一个线程依次处理这些事件,如果某个事件处理时间太长,会阻塞后面的事件的派发或处理。如果事件分发在后台线程,事件会立即执行处理
  • ASYNC :事件处理会在单独的线程中执行,主要用于在后台线程中执行耗时操作
    threadMode 默认为POSTING,事件的处理与发送在相同的线程中

sticky代表是否是粘性是事件 boolean类型

默认为false

priority代表事件的优先级

默认为0 值越大 优先级越高

在上篇文章中的第一小节,事件的注册中,有两个的地方需要注意下,这两个地方是对粘性事件的处理:

  /**
     * 解析订阅方法
     * @param 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) {
            // 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();
            //  解析方法类型
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        Class eventType = parameterTypes[0];
                        // 第一次进入返回true  重复注册返回false
                        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)) {
                    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");
            }
        }
    }

在这个解析订阅方法的方法里,我们拿到了订阅方法的注解类型以及对应的值,并保存到了findState对象中的subscriberMethods集合中中,并返回这个集合到register中

   public void register(Object subscriber) {
        Class subscriberClass = subscriber.getClass();
        // 返回注册的订阅者中的订阅方法集
        List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            // 遍历循环处理订阅者中的订阅方法
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

第二个要注意的地方就是在subscribe方法中:

   private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
       // 获取订阅方法的事件类型
        Class eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        /**
         * 通过订阅者中的订阅方法集 正常为情况下为null 如果多次注册 或者是在activity退出的时候没有反注册 就会报错
         * 并将订阅者与其中的订阅方法集储存起来
         */
        CopyOnWriteArrayList 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> 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).
                Set, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry, 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);
            }
        }
    }

回忆下上篇文章,这个方法主要是将订阅者中的订阅方法和事件类型进行分类储存

  • 以订阅者中的事件类型为key,该事件类型对应的该订阅者和订阅方法为value保存到subscriptionsByEventType中,
  • 以订阅者为key,该订阅者中的所有的事件类型的list集合为value保存到typesBySubscriber集合中。
    如果该事件是粘性事件,eventInheritance是由EventBuild赋值的,默认为true,具体作用没有我没有细究,不过在走完if方法体中的内容后,可以一起去看看else中的方法体。
    stickyEvents是在发送粘性事件的时候才赋值的
// 发送事件
EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!")); 
//  事件的处理  并保存到stickyEvents
  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);
    }

所以在首次注册本事件类型的粘性事件时,这个if代码块是不会走的,所以也就有了
可以先发送事件,而后再注册事件的情况 。 可以这么理解,事件只要发送了,无论是在发送前注册还是发送后注册,订阅者都能收到事件,这就是粘性事件。
假设我们已经注册了 过了 去看看if代码块中的逻辑:
for循环里面有个判断如果当前的事件类型与stickyEvents集合中的事件类是父>子关系的 那就执行checkPostStickyEventToSubscription方法。

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


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

后面就跟正常的事件发送一致了。
我们再来看下else代码块中的逻辑,他是直接从stickyEvents获取 然后执行很简单
这两个代码块的区别就是在for循环中的if判断语句,如果不是为if,那if代码块中的代码也完全可以写成else中的,我们结合下注释和isAssignableFrom 大致推测就是
如果post(A),A extends B implements C,那么onEvent(A)、onEvent(B)、onEvent(C)会被调用
具体的结果,还是由你们来校验吧。

粘性事件的处理到这里基本就完了,下面就是EventBus提供的一些其他的关于粘性事件的接口

获取某一个粘性事件,可以判断此粘性事件是否存在

 public  T getStickyEvent(Class eventType) {
        synchronized (stickyEvents) {
            return eventType.cast(stickyEvents.get(eventType));
        }
    }

删除某一个粘性事件(类类型的)

   public  T removeStickyEvent(Class eventType) {
        synchronized (stickyEvents) {
            return eventType.cast(stickyEvents.remove(eventType));
        }
    }

这个删除粘性事件是将stickyEvents集合中的粘性事件删除了 但是事件本身还是自带sticky = true属性,但是不影响

删除某一个粘性事件(对象类型的)

   public boolean removeStickyEvent(Object event) {
        synchronized (stickyEvents) {
            Class eventType = event.getClass();
            Object existingEvent = stickyEvents.get(eventType);
            if (event.equals(existingEvent)) {
                stickyEvents.remove(eventType);
                return true;
            } else {
                return false;
            }
        }
    }

两个都是一样的,只是入参不一样
删除所有粘性事件 很强

 public void removeAllStickyEvents() {
        synchronized (stickyEvents) {
            stickyEvents.clear();
        }
    }

你可能感兴趣的:(EventBus源码分析(二)——粘性事件)