EventBus的使用

2,043 阅读5分钟

大纲

EventBus是基于事件发布/订阅机制,实现的事件总线框架,通过解耦发布者和订阅者简化Android事件传递。可以灵活的在四大组件之前进行通讯,也可以通过指定线程模型,用于在不同线程间进行通讯。

EventBus:github地址

基本概念

EventBus主要有三个元素组成:

  • 事件——Event
  • 发布者——Publisher
  • 订阅者——Subscriber

订阅者(Subscriber)在注册register()之后,通过@Subscribe注解定义事件(Event)接收方法,来接收发布者(Publisher)发布的事件(Event),事件的发送和接收支持跨线程使用。

EventBus相对于Android传统的消息机制的优势在于,使用更简介、高效且支持灵活切换线程。

配置

  • 依赖配置

    implementation("org.greenrobot:eventbus:3.3.1")
    
  • 混淆配置

    -keepattributes *Annotation*
    -keepclassmembers class * {
        @org.greenrobot.eventbus.Subscribe <methods>;
    }
    -keep enum org.greenrobot.eventbus.ThreadMode { *; }
    
    # If using AsyncExecutord, keep required constructor of default event used.
    # Adjust the class name if a custom failure event type is used.
    -keepclassmembers class org.greenrobot.eventbus.util.ThrowableFailureEvent {
        <init>(java.lang.Throwable);
    }
    
    # Accessed via reflection, avoid renaming or removal
    -keep class org.greenrobot.eventbus.android.AndroidComponentsImpl
    

使用

简单使用

  • 注册/注销

    订阅者在接收事件之前需要先进行注册,事件接收通常与组件的生命周期是相关的,比如在Activity中通常在onCreate()中注册,在onDetroy()中注销。(当然也可以根据具体业务场景放到其他生命周期方法中。)

    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_second)
    
            registerEvent()
        }
    
        override fun onDestroy() {
            super.onDestroy()
            unRegisterEvent()
        }
    
        /**
         * 注册
         */
        private fun registerEvent(){
            EventBus.getDefault().register(this)
        }
    
        /**
         * 注销
         */
        private fun unRegisterEvent(){
            EventBus.getDefault().unregister(this)
        }
    }
    
  • 事件定义/发送

    EventBus事件发送支持任意类型的事件,订阅者也是根据不同的事件类型进行处理的,通常会将事件定义为xxxEvent结尾的事件类,如果要处理不同的事件只能定义不同的事件类。

    class StringEvent internal constructor(var msg: String) {
    
        internal fun getMsg() = msg
    
        internal fun setMsg(msg: String){
            this.msg = msg
        }
    }
    
  • 通过注解指定事件接收方法

    订阅者的事件接收方法需要通过@Subscribe()注解进行定义,在Activity中定义:

        @Subscribe(threadMode = ThreadMode.MAIN)
        fun event(event: StickyEvent){
          	//显示接收的事件
            textView3.text = event.msg
        }
    

    指定线程模型为ThreadMode.MAIN,因为运行在主线程中所以可以直接修改UI。

线程模型

EventBus支持如下几种线程模型,线程模型指定了订阅者在什么样的线程中处理实现,根据不同的使用场景可以选择不同的线程模型。如:上面的修改UI,可以指定为MAIN主线程。

//通过threadMode指定线程模型    
@Subscribe(threadMode = ThreadMode.MAIN)
fun event(event: StickyEvent){
   //显示接收的事件
   textView3.text = event.msg
}
  • ThreadMode.POSTING
    • 默认线程模型
    • 接收者与发布者同一个线程,事件同步传递,所以的事件订阅者同步接收事件消息。
    • 复用同一个线程,开销小。
  • ThreadMode.MAIN
    • 接收者在UI主线程中被调用。
    • 如果发布者是UI线程,订阅者也不会新起线程,如果不是,则会新起线程来处理订阅者事件。
    • 因为是UI线程,可以直接修改UI,但是不能进行耗时操作。
  • ThreadMode.MAIN_ORDERED
    • 接收者在UI主线程中被调用。
    • 接收者处理事件是串行执行,第一个执行完成后,第二个才会接收执行。
    • 同样可以直接修改UI,但是不能进行耗时操作。
  • ThreadMode.BACKGROUND
    • 后台进程,订阅者事件总在后台线程处理。
    • 如果发送者是UI线程,接收者会新建线程处理,否则在同一个线程处理。
  • ThreadMode.ASYNC
    • 异步线程,总是会新起线程处理接收者事件,来保证接收者和发布者总是在不同线程。
    • 内部使用了线程池进行处理,节省资源开销。

粘性事件

普通事件需要先注册订阅后,才会接收之后发送的事件。而粘性事件可以在注册订阅后,接收之前发送的事件。也就是说一个事件发送后,可以被后来才注册的订阅者接收。

使用

  • 发送

    //使用postSticky()发送粘性事件
    val event = StickyEvent("粘性事件")
    EventBus.getDefault().postSticky(event)
    
  • 接收

    //粘性事件的接收,同样需要在注解中添加 sticky = true
    @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
    fun event(event: StickyEvent){
       textView3.text = event.msg
    }
    

需要注意的是因为EventBus是通过事件类型处理不同事件的,如果同一类型的粘性事件被发送的多次,EventBus只会保留最后一个粘性事件。

因为粘性事件的特殊性,同一事件可能会被重复处理。对于不需要再被处理的粘性事件可以通过如下方法移除:

// 移除粘性事件
EventBus.getDefault().removeStickyEvent(stickyEvent)

小结

  • 优势
    • 简单,易用,功能强大。
    • 比广播效率更高。
  • 劣势
    • 实现简单,很容易被滥用。
    • 实现友好,可读性很差。
    • 为了接收事件,需要定义大量的Event类,后期大量的Event类难以管理。
    • 对类似组件化开发的多module支持不好,需要将Event类下沉到底层模块,反而破坏了模块独立性,增加了模块耦合度。
    • 功能强大,容易导致软件架构设计跑偏,尤其大型项目应该按照面向接口编程的思想进行设计。

扩展

其他的总线:

  • RxBus,只有一个类短短几十行代码,实现了同样功能强大的事件总线功能,是基于同样强大的Rxjava,如果项目已经使用了RxJava,那么Rxbus就是最佳选择。