EventBus源码分析​(三):Subscribe注解详解

QuibblerQuibbler 2021-09-27 约 15 分钟 2720 次阅读

EventBus源码分析(三):Subscribe注解详解

前面把EventBus源码看了一遍,回头再看一下EventBus初步使用一文中提到的Subscribe注解,就会更加理解这其中三个属性的含义及作用:

@Subscribe(threadMode = ThreadMode.BACKGROUND, sticky = true, priority = 100)
    public void onEvent(MessageEvent messageEvent) {
        //textView.setText(messageEvent.message);
    }

1、Subscribe注解

Subscribe注解定义如下:Subscribe注解只能作用在方法上;在编译后不仅被保留,在运行时获取该注解。

@Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    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;
    }

Subscribe注解有三个属性:

        ①threadMode:执行订阅方法的线程模型。默认POSTING,在事件发送线程执行订阅方法。

②sticky:sticky属性默认false,订阅方法不接受粘性事件。

③priority:订阅方法的优先级,方法优先级越高,越先接受到事件。默认优先级0。

1.1、threadMode

默认threadMode值为POSTING,在事件发送线程执行订阅方法。在EventBus事件分发postToSubscription(subscription, event, isMainThread)方法中会根据注解方法的threadMode选择合适的方式执行订阅方法。

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

1.2、sticky

订阅方法是否订阅粘性事件,默认不订阅粘性事件。在注册订阅对象的时候,subscribe(subscriber, subscriberMethod)方法中会根据注解中设置的sticky属性决定是否向新订阅对象的订阅方法分发已有粘性事件。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        ...
        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);
            }
        }
    }

如果注册的订阅方法订阅粘性事件,那么会将现有符合条件的粘性事件通过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());
        }
    }

1.3、priority

订阅方法的优先级,而不是事件的优先级。优先级越高,订阅方法就先执行。通过subscribe(subscriber, subscriberMethod)方法注册订阅对象的时候,就会根据订阅方法中设置的priority值大小,按优先级从大到小的顺序插入到现有的订阅方法集合中。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(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;
            }
        }
        ...
    }

2、解析Subscribe

在EventBus源码分析(一):构造及订阅一文的第2.1节,已经看过解析Subscribe注解的流程,借助SubscriberMethodFinder完成:


相关推荐

Airbnb开源项目
精选开源

Airbnb开源项目

Airbnb Engineering & Data ScienceAirflow Use Apache Airflow (incubating) to author workflows as directed acyclic graphs (DAGs) of tasks12,263Airpal Web UI for PrestoDB2,502Aerosolve A machine learning

2.3k
WilliamChart:轻量美观的 Android 图表库实战
精选开源

WilliamChart:轻量美观的 Android 图表库实战

WilliamChart:轻量美观的 Android 图表库实战数据可视化是运动、健康、金融、统计类 App 的刚需。MPAndroidChart 功能最全但偏重复杂,WilliamChart 走的则是"轻量 + 美观 + Kotlin 原生"路线——v3 用 Kotlin 完全重写,API 极简,一行 chart.animate(dataSet) 就能把数据画成带动画的折线/柱状/环形图,开箱即

175
sherpa解析:开源语音技术全家桶
精选开源

sherpa解析:开源语音技术全家桶

想在手机、树莓派甚至 RISC-V 开发板上离线跑语音识别,五年前还是个需要啃 Kaldi 源码的硬核课题。k2-fsa 组织的 sherpa 把这件事变成了"下载预训练模型、装个包、跑示例"的三步操作。这个以 Apache-2.0 协议开源的项目自我定位是"新一代 Kaldi 的语音转文本框架",它专注端到端模型的推理部署:transducer 与 CTC 两类架构,C++ 与 Python 双…

44