前言
- 在 Android 开发中,EventBus 事件总线机制十分常用;
- 今天,我将整理 EventBus 详细的使用教程,追求简单易懂又不失深度。如果能帮上忙,请务必点赞加关注,这真的对我非常重要。
目录
前置知识
这篇文章的内容会涉及以下前置 / 相关知识,贴心的我都帮你准备好了,请享用~
- APT: 【点赞催更】
- 注解:《Java | 注解(含 Kotlin)》
- 混淆:《Android | 代码混淆到底做了什么?》
1. EventBus 概述
- 定义:一套
Android / Java
事件订阅 / 发布框架,由 greenrobot 团队开源。
- 作用:在组件 / 线程间通信的场景中,将数据或事件传递给对应的订阅者。
- 为什么要使用
EventBus
(特点)? 在 Android 组件 / 线程间通信的应用场景中,EventBus 比传统的接口监听、Handler、Executors、LocalBroadcastManager 更简洁可靠,具体描述如下:
- 1、使用事件总线框架,实现事件发布者与订阅者松耦合;
- 2、提供了透明线程间通信,隐藏了发布线程与订阅线程间的线程切换。
- EventBus 相关概念 关于
EventBus
机制的相关概念如下:
2. 使用步骤
在分析 EventBus 的使用原理之前,我们先来介绍下 EventBus 的使用步骤。
2.1 步骤1:添加依赖
- 在 module 级
build.gradle
中添加依赖:
dependencies { def eventbus_version = '3.2.0' implementation "org.greenrobot:eventbus:$eventbus_version" } 复制代码
- 使用编译时索引时,还需要依赖 注解处理工具,注意:纯 Java 项目和 Kotlin 使用的注解处理工具不同:
- Java 项目使用
annotationProcessor
- Kotlin 项目使用
kapt
// Java: android { defaultConfig { javaCompileOptions { annotationProcessorOptions { arguments = [ eventBusIndex : 'com.have.a.good.MyEventBusAppIndex' ] } } } } dependencies { def eventbus_version = '3.2.0' implementation "org.greenrobot:eventbus:$eventbus_version" annotationProcessor "org.greenrobot:eventbus-annotation-processor:$eventbus_version" } 复制代码
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' // ensure kapt plugin is applied dependencies { def eventbus_version = '3.2.0' implementation "org.greenrobot:eventbus:$eventbus_version" kapt "org.greenrobot:eventbus-annotation-processor:$eventbus_version" } kapt { arguments { arg('eventBusIndex', 'com.have.a.good.MyEventBusAppIndex') } } 复制代码
2.2 步骤2:准备订阅者
订阅者需要实现订阅方法,并使用@Subscribe
注解修饰,具体要求如下:
举例:
@Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) { Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show(); } 复制代码
@Subscribe 注解参数中,threadMode
参数决定了使用的线程模型,目前一共有五种:
2.3 步骤3:注册与注销
在发布事件之前,需要先 注册订阅者。而在订阅者生命周期结束时,需要 注销订阅者。
举例:
@Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); } @Override public void onStop() { EventBus.getDefault().unregister(this); super.onStop(); } 复制代码
2.4 步骤4:发布事件
注册订阅者之后,就可以发布事件了,目前有两种事件:
- 调用
EventBus#post(Object)
发布普通事件 - 调用
EventBus#postSticky(Object)
发布粘性事件
举例:
EventBus.getDefault().post(new MessageEvent("Hello everyone!")); 复制代码
粘性事件 的特点如下:
举例:
1、订阅 @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) public void onEvent(MessageEvent event) { textField.setText(event.message); } 2、发布 EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!")); 3、获取粘性事件 MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class); if(stickyEvent != null) { 4、移除粘性事件 EventBus.getDefault().removeStickyEvent(stickyEvent); // do something. } 5、移除粘性事件 MessageEvent stickyEvent = EventBus.getDefault().removeStickyEvent(MessageEvent.class); if(stickyEvent != null) { // do something. } 复制代码
3. 编译时索引
EventBus 3.x
相较于EventBus 2.x
最大的改良就是 编译时索引
,注解生成器的源码可查看:EventBus 注解处理器源码,具体描述如下:
为了生成编译时索引
,首先需要在build.gradle
中配置索引文件,例如:
kapt { arguments { arg('eventBusIndex', 'com.have.a.good.MyEventBusAppIndex') } } 复制代码
编译时,注解处理器 将解析@Subscribe注解
修饰的方法,生成 索引类MyEventBusAppIndex.java
。你需要做的是在运行时构建时添加索引,例如:
EventBus eventBus = EventBus.builder() .addIndex(new MyEventBusAppIndex()) .build(); 复制代码
需要注意:索引类配置只对当前 module 有效,因此需要在每个包含订阅者的 module 级build.gradle
中添加索引类配置,例如:
// App module apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' // ensure kapt plugin is applied kapt { arguments { arg('eventBusIndex', 'com.have.a.good.MyEventBusAppIndex') } } dependencies { ... kapt "org.greenrobot:eventbus-annotation-processor:3.2.0" implementation project(path: ':base') } // Lib module apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' // ensure kapt plugin is applied kapt { arguments { arg('eventBusIndex', 'com.have.a.good.MyEventBusLibIndex') } } dependencies { ... api 'org.greenrobot:eventbus:3.2.0' kapt "org.greenrobot:eventbus-annotation-processor:3.2.0" implementation project(path: ':base') } 复制代码
以上配置将生成两个索引类文件,MyEventBusAppIndex.java
只包含App Module
中的订阅者索引,而MyEventBusLibIndex.java
只包含Lib Module
中的订阅者索引。
4. 构建者模式
构建者模式(Builder Pattern) 可以说是开源库的标配了,EventBus 也不例外。你可以使用EventBusBuilder
来构建 EventBus 实例,也可以直接调用EventBus.getDefault()
获得默认的 EventBus 实例;
- 1、异常处理配置
配置项 | 描述 | 默认值 |
logSubscriberExceptions | 订阅函数执行有异常时,打印异常信息 | true |
sendSubscriberExceptionEvent | 订阅函数执行有异常时,发布SubscriberExceptionEvent事件 | true |
throwSubscriberException | 订阅函数执行有异常时,抛出SubscriberException | false |
logNoSubscriberMessages | 事件无匹配订阅函数时,打印信息 | true |
sendNoSubscriberEvent | 事件无匹配订阅函数时,发布NoSubscriberEvent | true |
- 2、索引配置
配置项 | 描述 | 默认值 |
ignoreGeneratedIndex | 忽略订阅者索引 | false |
addIndex(SubscriberInfoIndex index) | 添加订阅者索引 | 无 |
- 3、事件订阅配置
配置项 | 描述 | 默认值 |
eventInheritance | 是否触发父类事件订阅函数 | true |
executorService(ExecutorService executorService) | 线程池 | Executors# newCachedThreadPool() |
strictMethodVerification | 是否严格验证订阅函数签名 | true |
skipMethodVerificationFor(Class<?> clazz) | 跳过方法签名验证 | 无 |
5. 混淆
ProGuard 和它的继承者 R8 都提供了压缩、优化、混淆和预校验四大功能。压缩和优化会移除未使用的类/方法/字段,混淆会使用无意义的简短名称重命名类/方法/字段。
@Subscribe 订阅方法是通过反射调用的,在编译时没有直接调用,如果不增加反混淆规则的话,在运行时会出现找不到方法名的情况。因此,EventBus
需要配置以下混淆规则:
-keepattributes *Annotation* // keep住所有被Subscribe注解标注的方法 -keepclassmembers class * { @org.greenrobot.eventbus.Subscribe <methods>; } -keep enum org.greenrobot.eventbus.ThreadMode { *; } 复制代码
如果使用了AsyncExecutor
,还需要配置混淆规则:
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent { <init>(java.lang.Throwable); } 复制代码
6. 总结
- EventBus 是一套 Android / Java 事件订阅 / 发布框架,用于在组件 / 线程间通信的场景中将数据或事件传递给订阅者,EventBus 的特点是可以实现事件订阅者与发布者解耦,以及透明地线程切换;
- EventBus 3.x 中,订阅者需要使用 @Subscribe 注解修饰订阅方法,可选五种线程模式(POSTING、MAIN、MAIN_ORDERED、BACKGROUND 和 ASYNC);
- 编译时索引的原理是通过编译时注解处理器生成索引表,记录事件 —— 订阅关系的映射,在运行时直接加载索引表。如果不使用编译时索引,在注册订阅者时就需要递归反射查找类本身与父类中使用@Subscribe注解修饰的方法,影响性能;
- @Subscribe 订阅方法是通过反射调用的,在编译时没有直接调用,如果不增加反混淆规则的话,在运行时会出现找不到方法名的情况。