大纲
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就是最佳选择。