文章目录
组件间通信,EventBus
EventBus的介绍
Android系统中的事件通信是 handler (消息机制) 和 BroadCastReceiver (广播机制), 通过它们可以实现组件之间的事件通讯。缺点在于,代码量多、组件之易产生藕合引用。这时候EventBus就展示出了他的优点
EventBus 是一种在软件开发中使用的发布/订阅(Pub/Sub)事件通信机制(假设你是一个读者,你对多个报纸感兴趣,但你并不想每天亲自去买每一份报纸。这时,你可以选择订阅报纸的服务。一旦你订阅了某份报纸,你就会定期收到该报纸的最新内容,无需亲自购买,,发布/订阅事件通信机制也是类似的概念。它基于一个中心组件(类似于报纸发行商)来管理事件的发布和订阅。其他组件(类似于读者****)可以订阅感兴趣的事件,一旦事件发生,中心组件会将事件通知给所有订阅者,而不需要订阅者主动轮询或查询事件。)****,用于组件之间的松耦合通信。EventBus 主要用于在单个应用程序内部的不同组件之间进行通信,比如在 Activity、Fragment、Service、以及普通 Java 类之间进行事件的发布和订阅。
在这种模式下,组件可以发送(发布)事件而不需要直接调用其他组件的方法,同样地,其他组件可以监听(订阅)这些事件而不需要知道谁是事件的发送者。这种方式可以降低组件之间的直接依赖,增强系统的灵活性和可扩展性。
EventBus的优点
EventBus 的作用主要体现在以下几个方面:
-
解耦:组件之间不需要相互了解,只需要通过事件来通信,从而实现解耦。
-
简化通信:在复杂的应用中,如果多个组件需要相互通信,使用 EventBus 可以简化这种通信过程,避免直接的多对多通信导致的复杂性。
-
异步处理:EventBus 允许事件的接收者在不同的线程中处理事件,这有助于进行异步编程,提高应用程序的响应性能。
-
动态监听:组件可以根据需要动态地订阅或取消订阅事件,这为动态环境下的交互提供了便利。
EventBus 的概念并不是新的,它在很多编程语言和框架中都有所体现。例如,在 Java 世界中,Google Guava 提供了一个流行的 EventBus 实现。在 Android 开发中,greenrobot 的 EventBus 是一个被广泛使用的库,用于在不同组件和层之间进行事件传递。
EventBus 的来历与观察者模式(Observer Pattern)紧密相关,观察者模式是一种设计模式,允许对象在状态变化时通知多个观察者对象。EventBus 可以被看作是观察者模式的一种变体或者扩展,它提供了更为灵活和动态的事件管理机制。
在使用 EventBus 时,也需要注意其可能带来的问题,比如过度使用 EventBus 可能会使得应用程序的逻辑变得难以追踪和维护,因为事件的发布和订阅可能分布在代码的不同部分。此外,大量的事件和监听器可能会导致内存泄露的风险,特别是在没有正确取消订阅的情况下。因此,在使用 EventBus 时需要谨慎考虑其适用场景,并确保良好的事件管理。
EventBus的缺点
核心缺点,他的初衷是为了降低耦合性,但是因为他的松散耦合,在大型项目中事件的流动可能会变得非常复杂,松散耦合会使跟踪和理解事件的流向成为一个问题,过度使用可能导致代码逻辑的不连贯性,从而使代码更加难以理解。某些事件的来源和处理可能变得难以追踪。调试问题可能会变得更加困难。
EventBus的理解与使用
三个部分
- 事件(Event):事件是在应用程序中传递的消息或信号。它可以是一个简单的 Java 对象,也可以是自定义的事件类。事件可以携带数据,用于在发布和订阅之间传递信息。
- 事件发布者(Event Publisher):事件发布者是产生并发布事件的组件。它负责将事件发送到 EventBus 中,通知所有对该事件感兴趣的订阅者。
- 事件订阅者(Event Subscriber):事件订阅者是对事件感兴趣并注册到 EventBus 中的组件。它通过定义事件处理方法来声明自己对特定事件的关注。当事件发布者发布一个事件时,事件订阅者会接收到相应的事件,并执行预定义的事件处理方法。
他们的关系与使用你可以很直观的在上面的图中感受到,也不难理解
五种线程模型
-
POSTING(默认):事件的发布和订阅在同一个线程中执行,即事件的发布会立即调用所有订阅者的事件处理方法。这种模型下,事件的发布是同步的,不涉及线程切换。
-
MAIN:事件的发布在主线程(UI 线程)中执行,适用于需要更新 UI 的事件处理。订阅者的事件处理方法会在主线程中执行,因此需要注意避免耗时操作阻塞主线程。
-
BACKGROUND:事件的发布和订阅在后台线程中执行,适用于耗时操作或需要在后台线程中执行的事件处理。事件的发布会创建一个新的后台线程执行。
-
ASYNC:事件的发布和订阅在独立的线程池中执行,适用于需要并行处理多个事件或需要在独立线程中执行的事件处理。事件的发布会创建一个新的线程池执行。
-
IMMEDIATE:事件的发布会立即调用订阅者的事件处理方法,但在当前线程中执行。如果当前线程不是事件的发布线程,那么事件处理方法会在当前线程中被排队等待执行。这种模型可以保证事件在发布线程中按顺序执行。
// 创建订阅者类 public class MessageSubscriber { @Subscribe(threadMode = ThreadMode.BACKGROUND) public void onMessageEvent(MessageEvent event) { } }
常见方法
register(Object subscriber)
: 注册订阅者,用于接收事件。unregister(Object subscriber)
: 取消注册订阅者,停止接收事件。- getDefault():获取EventBus的默认实例
post(Object event)
: 发布一个事件,让所有订阅者都能够接收到。postSticky(Object event)
: 发布一个粘性事件,让后续注册的订阅者也能够接收到该事件。removeStickyEvent(Object event)
: 移除指定的粘性事件。removeAllStickyEvents()
: 移除所有的粘性事件。
EventBus 使用
1.引入依赖
implementation 'org.greenrobot:eventbus:3.2.0'
2.定义事件
public class MessageWrap {
private String message;
public MessageWrap(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
这里我将其定义在了数据组件中后续在俩个组件中分别引用即可
3.发出消息
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button =findViewById(R.id.button);
if (true) { // 这两行必须写在init之前,否则这些配置在init过程中将无效
ARouter.openLog(); // 打印日志
ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
}
ARouter.init(getApplication());
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ARouter.getInstance().build("/first/MainActivity").navigation();
}
});
}
@Override
protected void onStart() {
super.onStart();
//注册EventBus
EventBus.getDefault().post(new MessageWrap("我想把这句话传过去"));
}
}
这里只是单纯的使用EventBus.getDefault().post()将其发送了出去
4.接收消息
@Route(path ="/first/MainActivity")
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.text);
EventBus.getDefault().register(this);//创建的时候注册EventBus
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this); // 在销毁时反注册EventBus
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void getMessageEvent(MessageWrap event)
{
String message = event.getMessage();
Toast.makeText(this, "jjj"+message,Toast.LENGTH_LONG).show();
}
}
@Subscribe这个注释的方法在post后执行,如果在发布 MessageWrap
事件的时候,目标活动(即 MainActivity
)尚未创建,那么该活动中的 getMessageEvent
方法不会被执行。因为 EventBus 会在事件发布后寻找已经注册的订阅者,然后调用相应的订阅方法。如果活动还未创建,订阅方法也就无法执行。
这里有看到@Subscribe
这个注解,他用于标记一个事件订阅者方法,指定该方法用于接收特定类型的事件。在该方法中,可以通过参数来接收传递的事件对象,并进行相应的处理。有三个参数
threadMode
:指定事件订阅方法的执行线程,默认为ThreadMode.POSTING
,即与发布事件所在的线程相同。其他可选值上面有讲priority
:指定事件订阅方法的优先级。值较高的方法会先被执行,默认为 0。sticky
:指定是否接收粘性事件。粘性事件是一种特殊的事件,它会一直存在于事件总线中,直到被消费。如果设置为true
,表示该方法可以接收粘性事件,默认为false
。
5.黏性事件
在EventBus中,普通事件(Non-Sticky Event)是通过post()
方法发布的,订阅者只能接收到在订阅之后发布的事件。所谓的黏性事件,就是指发送了该事件之后再订阅者依然能够接收到的事件。使用黏性事件的时候有两个地方需要做些修改。一个是订阅事件的地方,这里我们在先打开的Activity中注册监听黏性事件:
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void getMessageEvent(MessageWrap event)
{
String message = event.getMessage();
Toast.makeText(this, "jjj"+message,Toast.LENGTH_LONG).show();
}
另一个是发布事件的地方,这里我们在新的开的Activity中发布黏性事件。即调用EventBus的postSticky
方法来发布事件:
EventBus.getDefault().postSticky(new MessageWrap("我想把这句话传过去"));
按照上面的模式,我们先在第一个Activity中打开第二个Activity,然后在第二个Activity中发布黏性事件,并回到第一个Activity注册EventBus。根据测试结果,当按下注册按钮的时候,会立即触发上面的订阅方法从而获取到了黏性事件。
前面介绍方法的时候还有一个移除粘性事件的方法不知道你有没有注意到,既然有了就要使用
@Subscribe(threadMode = ThreadMode.MAIN)
public void getMessageEvent(MessageWrap event)
{
String message = event.getMessage();
Toast.makeText(this, "jjj"+message,Toast.LENGTH_LONG).show();
// 移除已处理的粘性事件
EventBus.getDefault().removeStickyEvent(event);
}
如果不移除粘性事件,它将一直保存在 EventBus 的粘性事件缓存中,这可能会导致一些意想不到的结果。例如,如果某个粘性事件被发送了多次,但只有最后一次发送的事件会被保存在缓存中,这可能会导致接收到的数据与预期不符。另外,未移除的粘性事件也可能会占用一定的内存空间,影响程序的性能和资源利用率。
6.优先级
在 EventBus 中,可以通过指定订阅者的优先级来控制事件的处理顺序。默认情况下,事件的处理是按照订阅者的注册顺序进行的,但可以通过设置订阅者的优先级来改变这个顺序。
在 EventBus 中,订阅者的优先级是通过 @Subscribe
注解的 priority
属性来指定的。priority
属性接收一个整数值,值越小表示优先级越高,即优先级为 0 的订阅者先于优先级为 1 的订阅者进行事件处理。
这里有几个地方需要注意:
- 只有当两个订阅方法使用相同的
ThreadMode
参数的时候,它们的优先级才会与priority
指定的值一致如果两个订阅方法的线程模式不同,那么它们的优先级就不能直接比较了。例如,如果一个方法的线程模式是MAIN
,另一个方法的线程模式是BACKGROUND
,那么无论它们的优先级如何设置,它们之间的处理顺序也是不确定的。; - 只有当某个订阅方法的
ThreadMode
参数为POSTING
的时候,它才能停止该事件的继续分发,该方法内部调用了event.cancelEventDelivery()
方法。这个方法可以用于在某个订阅方法中判断是否需要继续分发该事件,从而达到停止分发的效果。需要注意的是,只有在POSTING
线程模式下才能使用该方法,其他线程模式下调用该方法将不起作用。。
@Subscribe(priority = 0)
public void onEventA(EventA event) {
// 处理 EventA 事件,优先级为 0
}
@Subscribe(priority = 1)
public void onEventB(EventB event) {
// 处理 EventB 事件,优先级为 1
}
总的来说,EventBus 是一个非常有用的事件通信库,在组件之间进行松耦合通信方面发挥了重要作用。然而,在使用 EventBus 时需要注意其适用场景,并且合理管理事件,避免出现问题。如果正确使用,EventBus 能够简化组件间的通信,提高代码的可维护性和可读性,从而使得开发更加高效和便捷。总的来说,EventBus 是一个非常有用的事件通信库,在组件之间进行松耦合通信方面发挥了重要作用。然而,在使用 EventBus 时需要注意其适用场景,并且合理管理事件,避免出现问题。如果正确使用,EventBus 能够简化组件间的通信,提高代码的可维护性和可读性,从而使得开发更加高效和便捷。