首页 > 其他分享 >Android中EventBus简单使用

Android中EventBus简单使用

时间:2024-06-14 18:59:56浏览次数:10  
标签:EventBus message public Subscribe 简单 Android MessageEvent event

综述

消息总线又叫事件总线, 被广泛的应用于各类项目之中. 但是此处只概述Android体系中用到的框架. 为什么项目会需要一个消息总线呢? 一句话概括, 在大多数常见项目中, 随着项目变大, 项目可能出现大量的跨页面, 跨组件, 跨线程, 跨进程来传递消息与数据的需求. 为了更方便的直接通知到指定的页面实现具体的逻辑, 工程师们发展了消息总线体系来解决这一类问题, 因此如今我们可以在其它工程师的工作基础上简单的实现我们想要的功能.

现在是2023年, 从现在往前推10年, 这期间Android体系中被广泛使用的主流消息总线框架大致有如下几种:

  • 首先是Android原生的 Broadcast 以及 BroadcastReceiver 体系. 这是Android中最原始也最笨重的事件总线.
  • 接着是 GreenRobot 公司出品的 EventBus 和开发者们使用 RxJava 库封装的 RxBus 相互竞争. 一者简单易学上限低, 一者强大复杂难入门.
  • 然后流行的是使用官方出品的 Android Jetpack 库中的 livedata 组件封装的 LiveDataBus.
  • 最新开始广泛使用的消息总线则是使用SharedFlow封装的 FlowBus.

所以, 回到本文的核心, 从某种意义上说, Eventbus处于一个承上启下的位置. 相较于原生的广播体系它简化了组件间的通信, 提高了开发效率, 又因为其流行时间较早而广泛的应用于大量早期开发的android项目中, 没有 RxBus 强大的功能既是缺点也是优点, 因为这样也就没有过于复杂的上手难度. 唯一值得诟病的是消息发送和接收没有一个统一的管理或者配置中心, 一旦大规模使用会非常繁杂, 使得其在大型和超大型项目中难以维护.

因此, 我整理并记录这篇文章, 方便在日后需要时进行回顾.

概念

以下为GreenRobot官方给出的EventBus消息传递示意图

此图描述使用eventbus传递一次消息所涉及的四个角色, 即

  1. 发布者, 也就是Publisher, 负责发送消息.
  2. 订阅者, 也就是Subscriber, 负责接收消息.
  3. 事件, 也就是Event, 即存放信息的事件对象.
  4. EventBus, 可以理解为中转站, 事件从发布者通过EventBus发送到订阅者那里.

在使用时, 需要工程师自己实现的仅有前三个角色. 中转站本身EventBus库已经自行实现.

使用方法

添加依赖

使用前, 需要引入相关的库, 在gradle中加入

implementation("org.greenrobot:eventbus:3.3.1")
//或者是java版本
implementation("org.greenrobot:eventbus-java:3.3.1")

如果使用的是maven则在依赖中加入

<dependency>
    <groupId>org.greenrobot</groupId>
    <artifactId>eventbus-java</artifactId>
    <version>3.3.1</version>
</dependency>

其它的比如将jar包引入项目的做法此处略过.

简单使用

首先需要定义一个事件Event, 这个类一般单独定义

//java版本
public class MessageEvent {
 
    public final String message;
 
    public MessageEvent(String message) {
        this.message = message;
    }
}

然后定义一个订阅者, 这个方法一般会放在需要接受信息的类中, 以下两个示例分别对应使用主线程(UI线程)传递和处理事件, 以及使用当前发布者所在的线程(无论是哪个线程)传递和处理事件时的做法.

//java版本
// This method will be called when a MessageEvent is posted (in the UI thread for Toast)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
    Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
 
// This method will be called when a SomeOtherEvent is posted
@Subscribe
public void handleSomethingElse(SomeOtherEvent event) {
    doSomethingWith(event);
}

同时, 定义的订阅者方法的类需要被注册才能被eventbus知晓, 在有生命周期回调的类比如fragment或者activity中使用订阅者一般在生命周期中注册和注销订阅者所在的类. 而没有生命周期回调的类比如repo或者datasource, 一般会在初始化时注册订阅者, 在类被主动销毁时注销订阅者(或者干脆不注销, 使用单例模式加上app运行结束系统自动关闭).
注意: 订阅者方法必须为public, 且无论是缺了注册注销还是缺了订阅者方法都可能导致app报错

//java版本
@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}
 
@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

最后, 需要定义一个发布者, 发布者非常简单, 只是一条简单的指令, 在任何需要发送数据的地方直接调用即可

//java版本
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));

拓展功能介绍

订阅方式

在实现订阅者的时候除了默认的使用 @Subscriber 注解指定订阅者方法的做法之外, 还可以使用拓展功能同时指定消息传输的线程
使用如下方式

//默认方式
@Subscribe
public void handleSomethingElse(SomeOtherEvent event) {
    doSomethingWith(event);
}
//拓展方式

// Called in the same thread (default)
// ThreadMode is optional here
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessage(MessageEvent event) {
    log(event.message);
}

// Called in a separate thread
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessage(MessageEvent event){
    backend.send(event.message);
}

// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) {
    textField.setText(event.message);
}


// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
public void onMessage(MessageEvent event) {
    textField.setText(event.message);
}


// Called in the background thread
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessage(MessageEvent event){
    saveToDisk(event.message);
}
  • 其中使用 @Subscribe(threadMode = ThreadMode.POSTING) 等同于使用 @Subscribe, 或者说后者就是前者的简写.
  • 使用 @Subscribe(threadMode = ThreadMode.MAIN) 表示订阅者方法将在android的主线程即UI线程中被调用, 一般用来处理和UI有关的操作.
  • 使用 @Subscribe(threadMode = ThreadMode.MAIN_ORDERED) 将使得事件会严格的按照调用的顺序通过UI线程排队发送给订阅者.
  • 使用 @Subscribe(threadMode = ThreadMode.BACKGROUND) 表示如果发布者不是在主线程发布的事件则直接发送给订阅者, 如果发布方法在主线程发布的事件, eventbus将使用后台线程单独发送事件.
  • 使用 @Subscribe(threadMode = ThreadMode.ASYNC) 表示EventBus将始终使用另外的单独线程将事件发送给订阅者方法, 无论发布者是在哪个线程发送的事件.

粘性事件

粘性事件是一种特殊的事件, 常规来说, 一般发布者发送出去的事件如果发送后订阅者才注册, 那么新注册的订阅者是不会收到发送者发送的数据. 但是如果处于特殊场景下, 项目需要订阅者在注册后接收注册前已经发出的信息, 就可以使用粘性事件.

考虑以下假想场景:
一个项目需要和远程服务器进行连接并且隔一段时间动态获取数据并更新在activity界面, 所以使用了service进行持续的自动运行.
由于数据更新在ui上, 所以设置为activity处于onResume()时开启service, activity处于onPause()时停止service. 因为有时候用户会需要立刻刷新数据而不是等几十秒再刷新, 所以设置使用eventbus通知service终止当前的循环处理, 重新且立刻开始间隔一段时候就访问一次服务器数据的流程. 同时还存在另一个功能需求, 每次调用均需要动态申请系统权限并获取用户的许可.
好, 假设现在需要在获取用户许可后立刻通知服务器刷新. 那么就会存在获取用户许可时因为调用的是系统窗口, activity处于onstop()回调周期, service停止. 用户点击确定后, service可能已经开启也可能尚未重新初始化并注册订阅者.
所以, 此时通过权限申请回调运行发布者的指令会时不时无效.而在以上场景中, 使用粘性事件会是一种比较好的解决方案.

首先还是需要定义一个事件, 这一点和常规的事件的发送没有区别

//注意, 事件命名在语法上是自由的, 但是为了可读性, 一般会从XXXEvent改为StickyXXXEvent或者XXXStickyEvent
public class StickyMessageEvent {
 
    public final String message;
 
    public MessageEvent(String message) {
        this.message = message;
    }
}

定义一个发布者

//定义发布者
EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));

最后定义一个接收者

//定义接收者
@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}
 
// UI updates must run on MainThread
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {   
    textField.setText(event.message);
}
 
@Override
public void onStop() {
    EventBus.getDefault().unregister(this);    
    super.onStop();
}

额外的, 因为粘性事件会长期存放在eventbus中, 一般在接收之后需要将其清除

//一般放在onEvent()中获取事件并处理后再执行
MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
    // "Consume" the sticky event
    EventBus.getDefault().removeStickyEvent(stickyEvent);
    // Now do something with it
}

总结

以上就是eventbus的用法的简单整理, 一般性的使用差不多只需要这些功能. 其它的像是property, configuration, 取消发送之类的功能一般不会用到.
同时需要额外注意, 使用3.0之前的eventbus版本时, 在app打包时, 对eventbus相关方法进行混淆代码可能导致eventbus无法正常工作, 需要在混淆中单独设置规则以避开. 而3.0之后(包括3.0)的eventbus已经采用了新机制, 不需要对此进行额外的处理.

标签:EventBus,message,public,Subscribe,简单,Android,MessageEvent,event
From: https://www.cnblogs.com/dwcg/p/17933292.html

相关文章

  • 「C++」简单模拟
    这是一个公式:\[F_n=\dfrac{\left(\frac{1+\sqrt{5}}{2}\right)^n-\left(\frac{1-\sqrt{5}}{2}\right)^n}{\sqrt{5}}\]根据大家的数学经验可以知道这是一个计算斐波那契数列的公式,那么假设我们不知道这是一个斐波纳契数列的公式,只知道他是一个简单的数学计算公式,该怎么求这个公式......
  • 车载android开发 carservice(一)
    车载android开发carservice是什么?车载Android开发中的CarService是一个专门为汽车环境设计的系统服务。CarService通常是AndroidAutomotiveOS的一部分,提供一系列API和框架,允许开发人员构建与汽车相关的应用和服务。以下是CarService的一些主要功能和作用:车辆数据访问:C......
  • 04《android studio开发实战(第三版)》第七到十章阅读笔记
    第七章:持久化存储本章介绍了SharedPreferences的使用方法,它是一种轻量级的存储方案,用于保存简单的键值对数据,如用户设置和配置。 学习了如何创建SharedPreferences对象,使用getSharedPreferences()方法读取和写入数据,以及如何使用apply()和commit()提交修改。了解了如何在Andro......
  • 简单的Makefile文件解析
    Makefile文件解析#commonmakefileheader#"$(变量)"、"$makefile内置变量"表示变量值DIR_INC=../../include #头文件相对路径DIR_BIN=../../bin #可执行文件的相对路径DIR_LIB=../../libs #库的相对路径TARGET =iat_online_record_sample #目标变量BIN_TA......
  • Android Jetpack Compose入门教程(一)
    JetpackCompose是用于构建原生Android界面的新工具包。它使用更少的代码、强大的工具和直观的KotlinAPI,可以帮助您简化并加快Android界面开发。在本教程中,您将使用声明性的函数构建一个简单的界面组件。您无需修改任何XML布局,也不需要使用布局编辑器。相反,您只需......
  • 基于Android的礼品电商平台App设计与实现 毕业设计源码65516
                              摘 要在传统的商业模式中,对于礼品等商品,人们习惯于到各种商家店铺挑选购买。随着网络购物的广泛普及,和不断加快的时代节奏代中,人们不一定能为购买礼品腾出时间,更不会耐心挑选自己想礼品。......
  • Android WebSocket长连接的实现
    一、为什么需要WebSocket初次接触WebSocket的人,都会问同样的问题:我们已经有了HTTP协议,为什么还需要另一个协议?它能带来什么好处?答案很简单,因为HTTP协议有一个缺陷:通信只能由客户端发起。举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询......
  • 安卓应用开发——Android Studio中通过id进行约束布局
    在Android开发中,布局通常使用XML文件来描述,而约束(如相对位置、大小等)可以通过多种方式实现,但直接使用ID进行约束并不直接对应于Android的传统布局系统(如LinearLayout、RelativeLayout等)。然而,从AndroidStudio3.0开始,引入了ConstraintLayout,它允许你通过ID来定义视图之间的约......
  • ACCESS 在数据表中实现简单计算
    PrivateSub权重_KeyDown(KeyCodeAsInteger,ShiftAsInteger)IfKeyCode<>vbKeyReturnAndKeyCode<>vbKeyUpAndKeyCode<>vbKeyDownAndvbKeyTabThenExitSub权重.Text=M1.CalculateExpression(权重.Text)EndSub'公共函数F......
  • 舵机堵转的危害与简单解决方式
    舵机的堵转保护是一种安全特性,用于防止舵机在遇到阻力无法正常旋转时受到损害。当舵机尝试移动到某个位置但因为外部阻力(如卡住或碰撞)而无法完成动作时,它会持续施加力直至达到其最大扭矩。如果没有堵转保护,这种情况可能会导致舵机内部的电机过热,甚至烧毁。堵转保护通过监测电......