目录
介绍
EventBus产生的背景
EventBus工作流程图解
EventBus的优势
EventBus缺点
EventBus 的一些关键概念和用法:
使用 EventBus 的基本流程:
EventBus环境配置
EventBus的五种线程模式
EventBus的使用
EventBus事件三部曲
创建一个事件类
注册EventBus
创建订阅者发起通知
效果
MainActivity
编辑
在FirstActivity中发送消息,MainActivity也能接收到
Subscribe注解介绍
黏性sticky
效果
优先级priority
效果
注:本文基于组件化项目举例并使用
Android组件化基础(一)——概述与基本配置-CSDN博客
Android组件化基础(二)——组件间的通信-CSDN博客
Android系统内置的事件通讯存在缺点:
Android系统中的事件通信则是 handler (消息机制) 和 BroadCastReceiver (广播机制), 通过它们可以实现组件之间的事件通讯。缺点在于,代码量多、组件之易产生藕合引用。
当我们进行项目开发的时候,经常会遇到组件与组件之间、组件与后台线程之间的通信, 比如:子线程中执行数据请求,数据请求成功后,通过 Handler 或者 BroadCast 来通知UI更新。 两个Fragment之间可以通过Listener进行通信,但是问题来了,当程序越来越大时,就会要写很多的代码, 而且导致代码严重的耦合问题。为此 ,EventBus 应运而生。
EventBus的官方文档:EventBus: Events for Android - Open Source by greenrobot
Publisher使用post发出一个Event事件,Subscriber在onEvent()函数中接收事件。
EventBus 是一个开源的 Android 事件发布/订阅框架,用于简化组件或者线程之间的通信。它采用了观察者设计模式,允许不同组件之间通过发布和订阅事件来实现解耦和松散耦合,从而提高代码的可维护性和可扩展性。
1,简化组件之间的通讯方式
2,对通信双方进行解藕
3,使用ThreadMode灵活切换工作线程
4,速度快、性能好
5,库比较小,不占内存
1、使用的时候有定义很多event类
2、event在注册的时候会调用反射去遍历注册对象的方法在其中找出带有@subscriber标签的方法,性能不高。
3、需要自己注册和反注册,如果忘了反注册就会导致内存泄漏
事件(Event):在 EventBus 中,事件是一个普通的 Java 对象,用于在不同组件之间传递信息。
订阅者(Subscriber):订阅者是对事件感兴趣并通过订阅(注册)来接收事件的组件或类。订阅者需要定义一个或多个用于处理事件的方法。
发布者(Publisher):发布者是负责发布事件的组件或类。它们通过事件的类型来标识事件,并将事件发送给所有订阅了该类型事件的订阅者。
订阅(Subscribe):订阅是指让订阅者注册到 EventBus 中,以便接收特定类型的事件。通过注解或代码方式将订阅者注册到 EventBus。
事件处理方法:订阅者需要定义用于处理特定类型事件的方法。这些方法使用特定的注解来标记,例如 @Subscribe
。当事件被发布时,EventBus 将自动调用对应订阅者的处理方法。
线程模式(Thread Mode):EventBus 支持不同的线程模式,用于控制事件处理方法在哪个线程被调用。例如,ThreadMode.MAIN
表示事件处理方法在主线程中执行,ThreadMode.BACKGROUND
表示在后台线程中执行。
定义事件类:创建一个用于传递信息的事件类。
注册订阅者:在订阅者中注册到 EventBus,标识对哪些事件感兴趣,并定义事件处理方法。
发布事件:在发布者中创建事件对象并使用 EventBus 发布事件。
处理事件:EventBus 将自动将事件分发给订阅者,并调用对应的事件处理方法。
EventBus 提供了简洁的 API 和灵活的配置选项,使得在 Android 应用程序中实现组件间的事件通信变得更加简单和高效。
1,依赖导入
在app module的builde.gradle文件中导入依赖库:
imlementation ‘org.greenrobot:eventbus:3.2.0’
2,配置混淆
必须配置,否则会出现,debug环境正常,release环境接收不到事件的问题
在使用的组件或者包下的proguard-rules.pro文件内修改
-keepattributes *Annotation*
-keepclassmembers class * {
@org.greenrobot.eventbus.Subscribe ;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
(java.lang.Throwable);
}
在事件订阅者使用@Subscribe注解标记回调函数时,需要指定线程模式,未指定默认为POSTING。
ThreadMode.POSTING:默认线程模式。订阅者方法将会在发布事件的同一线程中被调用。事件的传递是同步的,一旦发布事件,所有该模式的订阅者方法都将被调用。这种线程模式以为着最少的性能开销,因为他避免了线程切换。因此对于耗时短并且不需要主线程的简单任务,推荐使用这种模式。使用这种模式的事件处理应该快速返回,因为有可能事件是在主线程中发布的。
ThreadMode.MAIN:订阅者方法将会在主线程中调用。如果发布事件的线程也是主线程,那么和指定为ThreadMode.POSTING一样,不需要切换线程运行。使用这种模式的事件处理必须快速返回,否则会阻塞主线程。
ThreadMode.MIAN_ORDERED:订阅者方法会在主线程中被调用。发布者发布的事件会进入队列,依照先后顺序依次发送给订阅者。所以叫Ordered。
例如:如果在MAIN线程模式中的事件处理(称为第一事件)中去发布另外一个事件(称为第二事件),第二事件的订阅者的线程模式也是MAIN,那么第二事件会在第一事件处理结束前就开始处理。但是如果第二事件的订阅者的线程模式是MAIN_ORDERED,那么在第一事件结束的稍后的事件就会开始处理第二事件。
同样,使用此模式的事件处理必须快速返回,以免阻塞主线程。
ThreadMode.BACKGROUND:订阅者方法将在后台线程中被调用。如果发布事件的线程不是主线程,则将直接在发布线程中调用事件处理方法;如果是主线程,那么EventBus会创建一个单独的后台线程,该线程将按照顺序传递所有事件。使用此这种模式的事件处理应该尽量快速返回,避免阻塞后台线程。使用这种线程模式不会在主线程中处理事件。
ThreadMode.ASYNC:订阅者方法将会在一个单独的线程中被调用。这个线程始终独立于发布线程和主线程。如果事件处理方法的执行可能需要一些时间,例如网络访问,则应该使用此模式。
Subscriber、Event、Publisher。
Subscriber —— EventBus的register方法,会接收到一个Object对象。
Event —— EventBus的post()方法中传入的事件类型 (可以是任意类型)。
Publisher —— EventBus的post()方法。
这里我在基础组件下创建
package com.example.module.libbase;
public class Msg {
public int id;
public String msg;
public Msg(int id, String msg) {
this.id = id;
this.msg = msg;
}
@Override
public String toString() {
return "id=" + id + ", msg='" + msg;
}
}
在需要订阅事件的模块中,注册EventBus
@Route(path = "/app/MainActivity")
public class MainActivity extends AppCompatActivity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
}
@Override
protected void onStart() {
super.onStart();
if (!EventBus.getDefault().isRegistered(this))
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
EventBus.getDefault().unregister(this);
super.onDestroy();
}
//接收事件
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true,priority = 1)
public void ReceiveMessage1(Msg msg){
text1.setText(msg.toString());
}
//接收事件
@Subscribe(threadMode = ThreadMode.POSTING,sticky = true,priority = 1)
public void ReceiveMessage2(Msg msg){
text2.setText(msg.toString());
}
//接收事件
@Subscribe(threadMode = ThreadMode.BACKGROUND,sticky = true,priority = 1)
public void ReceiveMessage3(Msg msg){
runOnUiThread(new Runnable() {
@Override
public void run() {
text3.setText(msg.toString());
}
});
}
//接收事件
@Subscribe(threadMode = ThreadMode.ASYNC,sticky = true,priority = 1)
public void ReceiveMessage4(Msg msg){
runOnUiThread(new Runnable() {
@Override
public void run() {
text4.setText(msg.toString());
}
});
}
//接收事件
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED,sticky = true,priority = 1)
public void ReceiveMessage5(Msg msg){
text5.setText(msg.toString());
}
}
使用eventbus.post(eventMessage) 或者 eventbus.postSticky(eventMessage)来发起事件
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
EventBus.getDefault().post(new Msg(111,"This is MainActivity"));
}
});
发送消息111
发送消息333
Subscribe是EventBus自定义的注解,共有三个参数(可选):threadMode、boolean sticky、int priority。 完整的写法如下:
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true,priority = 1)
public void onReceiveMsg(EventMessage message) {
Log.e(TAG, "onReceiveMsg: " + message.toString());
}
threadMode在之前已经介绍过了,接下来我们介绍 黏性sticky 和 优先级priority。
sticky是一个boolean类型,默认值为false,默认不开启黏性sticky特性,那么什么是sticky特性呢?
上面的例子都是对订阅者 (接收事件) 先进行注册,然后在进行post事件。那么sticky的作用就是:订阅者可以先不进行注册,如果post事件已经发出,再注册订阅者,同样可以接收到事件,并进行处理。
其实就是在sticky场景下,EventBus对事件进行了保存而已。
修改MainActivity的代码
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
EventBus.getDefault().postSticky(new Msg(111,"This is MainActivity"));
}
});
可以注意到post被改成了postSticky。
没改成postSticky之前,MainActivity发送消息FirstActivity无法收到,因为没有注册EventBus
修改后
priority优先级,是一个int类型,默认值为0。值越大,优先级越高,越优先接收到事件。
值得注意的是,只有在post事件和事件接收处理,处于同一个线程环境的时候,才有意义。
修改代码
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true,priority = 10)
public void ReceiveMessage1(Msg msg){
text1.setText(msg.toString());
}
@Subscribe(threadMode = ThreadMode.POSTING,sticky = true,priority = 8)
public void ReceiveMessage2(Msg msg){
text2.setText(msg.toString());
EventBus.getDefault().cancelEventDelivery(msg);//取消传递消息
}
@Subscribe(threadMode = ThreadMode.BACKGROUND,sticky = true,priority = 6)
public void ReceiveMessage3(Msg msg){
runOnUiThread(new Runnable() {
@Override
public void run() {
text3.setText(msg.toString());
}
});
}
@Subscribe(threadMode = ThreadMode.ASYNC,sticky = true,priority = 4)
public void ReceiveMessage4(Msg msg){
runOnUiThread(new Runnable() {
@Override
public void run() {
text4.setText(msg.toString());
}
});
}
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED,sticky = true,priority = 1)
public void ReceiveMessage5(Msg msg){
text5.setText(msg.toString());
}
我在优先级为8的订阅者接收消息后添加了该代码:EventBus.getDefault().cancelEventDelivery(msg);取消了信息传递。
可以看到,第二个TextView之后都没变化
另外,cancelEventDelivery
方法只在事件处理方法内部的事件发布线程上调用,即取消消息传递和发送消息的得是同一个线程。如果cancelEventDelivery 方法放在优先级为6的订阅者里执行,会报错。
上一篇:Android组件化基础(二)——组件间的通信-CSDN博客
本文参考:EventBus详解 (详解 + 原理)-CSDN博客
EventBus的基本使用-CSDN博客