当前位置: 首页 > 工具软件 > Sticky Plugin > 使用案例 >

深入理解EventBus - ThreadMode、Sticky Event等

梁丘扬
2023-12-01

        在 深入理解EventBus - 基本使用 已经初步了解了EventBus如何使用,下面从Thread Mode、Sticky Event、EventBus单例模式创建等方面,深入了解EventBus的使用,以满足各种场景的使用。

Configuration

        在 深入理解EventBus - 基本使用中,我们获取EventBus单例对象是通过EventBus.getDefault()方法。此时,EventBus对象的设置都是默认的。当然,大部分情况都是可以满足。都说小概率事件年年有。假如分发的事件,没有订阅者时,之前说过,会报异常。若不想报异常,让EventBus保持静默,不做扫描,该如何处理呢?那就不得不说EventBuilder,这个通过Builder模式创建EventBus对象了。下面先看EventBuilder常用的方法

        EventBus build():基于当前EventBus配置创建EventBus
        EventBus installDefaultEventBus():创建默认的EventBus对象,相当于EventBus.getDefault()。
        
        EventBuilder addIndex(SubscriberInfoIndex index): 添加由EventBus“注释预处理器生成的索引
        EventBuilder eventInheritance(boolean eventInheritance):默认情况下,EventBus认为事件类有层次结构(订户超类将被通知)
        EventBuilder executorService(java.util.concurrent.ExecutorService executorService):定义一个线程池用于处理后台线程和异步线程分发事件
        EventBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex):强制使用事件反射,即使事件已被设置索引
        EventBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages):打印没有订阅消息,默认为true
        EventBuilder logSubscriberExceptions(boolean logSubscriberExceptions):打印订阅异常,默认true
        EventBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent):设置发送的的事件在没有订阅者的情况时,EventBus是否保持静默,默认true
        EventBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent):发送分发事件的异常,默认true
        EventBuilder skipMethodVerificationFor(java.lang.Class<?> clazz) 在3.0以前,接收处理事件的方法名以onEvent开头,方法名称验证避免不是以此开头。
                                                                                                            使用此方法用以排除用户类
        EventBuilder strictMethodVerification(boolean strictMethodVerification) 启用严格的方法验证(默认:false)
        EventBuilder throwSubscriberException(boolean throwSubscriberException) 如果onEvent***方法出现异常,是否将此异常分发给订阅者(默认:false)
         根据EventBuilder的API,我们可以根据使用情况,创建我们所需要的EventBus对象了。从官方文档上,看了下面的两个例子。当然,你也可以自己写两个。
        1.若分发的事件没有订阅者,EventBus保持静默

        EventBus eventBus = EventBus.builder()

                                     .logNoSubscriberMessages(false)
                                     .sendNoSubscriberEvent(false).build();
        2.EventBus捕获来自的onEvent方法抛出的异常,并发送订户例外事件可以但不必被处理
        EventBus eventBus = EventBus.builder().throwSubscriberException(true).build();

ThreadMode

<span style="font-size:14px;">public enum ThreadMode {
    POSTING,
    MAIN,
    BACKGROUND,
    ASYNC
}</span>
        看上面的ThreadMode源码可以看出来,ThreadMode实际上就是一个枚举类型,定义了POSTING 、MAIN 、BACKGROUND、ASYNC等几个常量。这几个常量,在EventBus整个框架,到底起什么作用呢?下面我们一一解析:

ThreadMode: POSTING

        事件的处理在和事件的发送在相同的进程,所以事件处理时间不应太长,不然影响事件的发送线程,而这个线程可能是UI线程.

        @Subscribe(threadMode = ThreadMode.POSTING)
        public void onMessage(MessageEvent event) {
        log(event.message);
        }

ThreadMode: MAIN

        事件的处理会在UI线程中执行,事件处理不应太长时间

            @Subscribe(threadMode = ThreadMode.MAIN)
            public void onMessage(MessageEvent event) {
            textField.setText(event.message);
            }

ThreadMode: BACKGROUND

        事件的处理会在一个后台线程中执行,尽管是在后台线程中运行,事件处理时间不应太长。如果事件分发在主线程,件会被加到一个队列中,由一个线程依次处理这些事件,如果某个事件处理时间太长,会阻塞后面的事件的派发或处理。如果事件分发在后台线程,事件会立即执行处理。

  @Subscribe(threadMode = ThreadMode.BACKGROUND)
        public void onMessage(MessageEvent event){
            saveToDisk(event.message);
        }

ThreadMode: ASYNC

       事件处理会在单独的线程中执行,主要用于在后台线程中执行耗时操作,每个事件会开启一个线程(有线程池),但最好限制线程的数目。

        @Subscribe(threadMode = ThreadMode.ASYNC)
        public void onMessage(MessageEvent event){
            backend.send(event.message);
        }

Sticky Event - 粘性事件

        在Android开发中,Sticky事件只指事件消费者在事件发布之后才注册的也能接收到该事件的特殊类型。Android中就有这样的实例,也就是Sticky Broadcast,即粘性广播。正常情况下如果发送者发送了某个广播,而接收者在这个广播发送后才注册自己的Receiver,这时接收者便 无法接收到刚才的广播,为此Android引入了StickyBroadcast,在广播发送结束后会保存刚刚发送的广播(Intent),这样当接收者注册完Receiver 后就可以接收到刚才已经发布的广播。这就使得我们可以预先处理一些事件,让有消费者时再把这些事件投递给消费者。

使用实例

        1.分发粘性事件
        EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));
        2. 创建新的Activity,并在Activity中注册EventBus,订阅已分发的粘性事件
        @Override
        public void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
        }

        @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
        public void onEvent(MessageEvent event) {
            // UI updates must run on MainThread
            textField.setText(event.message);
        }

        @Override
        public void onStop() {
        EventBus.getDefault().unregister(this);
        super.onStop();
        }

获取和删除粘性事件

       在订阅者订阅粘性事件时,会自动匹配最后发送的粘性事件。在实际使用过程中,为了便于手动检查粘性事件,有可能会获取或者删除当前粘性事件。
    MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);
    // 判断此粘性事件是否存在
    if(stickyEvent != null) {
        // 若粘性事件存在,将其删除
        EventBus.getDefault().removeStickyEvent(stickyEvent);
    }
        查看EventBus API,可以看到removeStickyEvent(java.lang.Class<T> eventType)的返回值是<T> T,此方法的解释是删除并获取当前的粘性事件。所以上述示例,可以修改成:
  
 MessageEvent stickyEvent = EventBus.getDefault().removeStickyEvent(MessageEvent.class);
    // Better check that an event was actually posted before
    if(stickyEvent != null) {
    // Now do something with it
    }    

Priorities - 订阅事件处理优先级

       在大多数情况下,EventBus并不需要订阅者对事件处理时,存在优先级的情况,甚至是取消分发的事件。但,在特殊的情况下,会出现事件订阅处理的优先级。比如,同一事件可能会引发一些UI逻辑,此时事件的优先级显得尤为重要。此时,该如何处理呢?订阅事件处理的优先级,是在订阅者订阅时进行声明的,优先级比较好的订阅者可以取消分发的事件,同时优先级比较低的订阅者将不会再收到此取消的事件 。
       具体示例如下:
    @Subscribe(priority = 100)
    public void onEvent(MessageEvent event) {
        …
        // 取消分发的事件
        EventBus.getDefault().cancelEventDelivery(event) ;
    }

EventBus processor - 订阅者索引

        订阅者索引(subscriber index),是EventBus的新特性。它是一个可选的优化,提高了EventBus注册的效率。
        具体使用如下:
        1.gradle配置
            buildscript {
                dependencies {
                    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
                }
            }
            apply plugin: 'com.neenbedankt.android-apt'
            dependencies {
                compile 'org.greenrobot:eventbus:3.0.0'
                apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
            }
            apt {
                arguments {
                    eventBusIndex "com.example.myapp.MyEventBusIndex"
                }
            }
        2.使用索引(自定义)
        /** This class is generated by EventBus, do not edit. */
        public class MyEventBusIndex implements SubscriberInfoIndex {
            private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

            static {
                SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();

                putIndex(new SimpleSubscriberInfo(com.whoislcj.testhttp.MainActivity.class, true, new SubscriberMethodInfo[] {
                    new SubscriberMethodInfo("onDataSynEvent", com.whoislcj.testhttp.eventBus.DataSynEvent.class,
                            ThreadMode.MAIN, 100, false),
                    new SubscriberMethodInfo("onDataSynEvent1", com.whoislcj.testhttp.eventBus.TestEvent.class, ThreadMode.MAIN,
                            0, true),
                }));

            }

            private static void putIndex(SubscriberInfo info) {
                SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
            }

            @Override
            public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
                SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
                if (info != null) {
                    return info;
                } else {
                    return null;
                }
            }
        }
        3.将索引添加至EventBus单例中
        EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();

ProGuard - 代码混淆

    -keepattributes *Annotation*
    -keepclassmembers class ** {
        @org.greenrobot.eventbus.Subscribe <methods>;
    }
    -keep enum org.greenrobot.eventbus.ThreadMode { *; }

    # Only required if you use AsyncExecutor
    -keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
        <init>(java.lang.Throwable);
    }

         到此为止,EventBus的使用基本上算完成了,相信你一定会开启美妙的Android通信旅程,再也不用为繁琐的回调而烦恼了,再也不用Activity和Fragment之间的通信烦恼了。有兴趣看EventBus源码的童鞋,可以跟随鸿洋大神的脚步 Android EventBus源码解析 带你深入理解EventBus


参考资料:

        1.Android EventBus源码解析 带你深入理解EventBus

        2.官方文档

        3.Android消息传递之EventBus 3.0使用详解(三)

 类似资料: