深入解析:RxJava在Android中的应用

news/2025/9/17 18:25:03/文章来源:https://www.cnblogs.com/ljbguanli/p/19097272

深入解析:RxJava在Android中的应用

文章目录

    • 1.1 与 Retrofit 结合
    • 1.2 与 MVP/MVVM 架构结合
      • 1.2.1 与 MVP (Model-View-Presenter) 结合
      • 1.2.2 RxJava 与 MVVM (Model-View-ViewModel) 结合
    • 1.3. RxBus (事件总线)
    • 1.4. 错误处理与资源管理
    • 1.5 复杂任务编排
      • 1.5.1 并行任务合并(zip 操作符)
      • 1.5.2. 顺序任务合并(concat 操作符)
      • 1.5.3. 动态数据流合并(merge 操作符)
      • 1.5.4. 最新数据合并(combineLatest 操作符)
    • 1.6 UI 事件处理
      • 1.6.1 防抖优化搜索输入:

将 RxJava 融入到实际项目架构中,解决复杂问题。

1.1 与 Retrofit 结合

Retrofit 官方支持返回 Observable 或 Flowable,是处理网络请求的黄金搭档。

public interface ApiService {
@GET("users/{id}")
Observable getUser(@Path("id") int id);
}
// 使用
apiService.getUser(123)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
user -> { /* 处理成功 */ },
error -> { /* 处理错误 */}
);

1.2 与 MVP/MVVM 架构结合

将 RxJava 与 MVP 或 MVVM 架构结合使用,是现代 Android(以及 Java Swing)开发中的最佳实践之一。这种结合能充分发挥各自的优势:架构模式负责清晰的职责分离和可测试性,而 RxJava 则优雅地处理异步、事件流和复杂的线程切换。

1.2.1 与 MVP (Model-View-Presenter) 结合

MVP 的核心思想是将 Activity/Fragment (View) 从繁重的业务逻辑中解放出来,使其只负责 UI 的展示和用户交互的传递。Presenter 作为中间层,负责处理业务逻辑并与 Model 层交互。

// 1. 定义 View 接口
public interface UserView {
void showLoading();
void hideLoading();
void showUsers(List<
User> users);
void showError(String message);
}
// 2. Model 层 (返回 RxJava 数据流)
public class UserModel
{
private ApiService apiService;
// 假设使用 Retrofit
public Observable<
List<
User>
> getUsers() {
return apiService.getUsers() // 返回 Observable<List<User>>.subscribeOn(Schedulers.io());// 在 IO 线程执行网络请求}}// 3. Presenter 层 (核心: 处理 RxJava 链)public class UserPresenter{private UserView view;private UserModel model;private CompositeDisposable disposables = new CompositeDisposable();public UserPresenter(UserView view, UserModel model) {this.view = view;this.model = model;}public void loadUsers() {view.showLoading();// 建立 RxJava 订阅disposables.add(model.getUsers().observeOn(AndroidSchedulers.mainThread()) // 切换到主线程更新 UI.subscribe(users ->{view.hideLoading();view.showUsers(users);// 成功: 更新 UI},error ->{view.hideLoading();view.showError("加载失败: " + error.getMessage());// 失败: 显示错误}));}// 在 Activity/Fragment onDestroy 时调用,防止内存泄漏public void onDestroy() {disposables.clear();}}// 4. View 层 (Activity/Fragment)public class UserActivityextends AppCompatActivity implements UserView {private UserPresenter presenter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_user);// 初始化 Presenterpresenter = new UserPresenter(this, new UserModel());// 触发加载presenter.loadUsers();}@Overrideprotected void onDestroy() {super.onDestroy();presenter.onDestroy();// 关键: 取消所有订阅}// 实现 View 接口方法@Overridepublic void showLoading() {// 显示进度条}@Overridepublic void hideLoading() {// 隐藏进度条}@Overridepublic void showUsers(List<User> users) {// 更新 RecyclerView 或 ListView}@Overridepublic void showError(String message) {// 弹出 Toast 或 Snackbar}}
  • 优势:
    职责清晰: View 只管展示,Presenter 处理逻辑和数据流。
    易于测试: Presenter 不依赖 Android 组件,可以方便地进行单元测试。
    异步处理优雅: RxJava 完美解决了网络请求、数据库操作等异步问题。

  • 挑战:
    内存泄漏: 必须妥善管理 Disposable,在生命周期结束时取消订阅。
    Presenter 膨胀: 如果业务逻辑复杂,Presenter 可能会变得非常庞大。可以通过引入 Interactor (或称 Use Case) 层来分担业务逻辑。

1.2.2 RxJava 与 MVVM (Model-View-ViewModel) 结合

MVVM 通过 数据绑定 (Data Binding) 或 LiveData/StateFlow 将 View 与 ViewModel 解耦。View 通过观察 ViewModel 中的数据变化来自动更新 UI,ViewModel 则负责准备和管理这些数据。

RxJava 可以作为 ViewModel 内部处理异步数据流的强大工具,最终将结果暴露给 View。由于 Android 官方推荐在 MVVM 中使用 LiveData,而 LiveData 本身不是响应式流,我们通常使用 LiveDataReactiveStreams 工具类进行桥接。

// 1. Model 层 (同 MVP)
public class UserModel
{
public Observable<
List<
User>
> getUsers() {
return apiService.getUsers()
.subscribeOn(Schedulers.io());
}
}
// 2. ViewModel 层 (核心: 使用 RxJava 处理逻辑,输出 LiveData)
public class UserViewModel
extends ViewModel {
private UserModel model;
// 暴露给 View 的 LiveData
private MutableLiveData<
List<
User>
> usersLiveData = new MutableLiveData<
>();
private MutableLiveData<
Boolean> loadingLiveData = new MutableLiveData<
>();
private MutableLiveData<
String> errorLiveData = new MutableLiveData<
>();
// 提供 LiveData 给 View 观察
public LiveData<
List<
User>
> getUsers() {
return usersLiveData;
}
public LiveData<
Boolean> isLoading() {
return loadingLiveData;
}
public LiveData<
String> getError() {
return errorLiveData;
}
public UserViewModel(UserModel model) {
this.model = model;
}
public void loadUsers() {
loadingLiveData.setValue(true);
// 将 RxJava Observable 转换为 LiveData
LiveData<
List<
User>
> liveData = LiveDataReactiveStreams.fromPublisher(
model.getUsers()
.toFlowable(BackpressureStrategy.LATEST) // 转换为 Flowable 以支持背压
.observeOn(AndroidSchedulers.mainThread()) // 确保在主线程发射
);
// 订阅这个 LiveData
liveData.observeForever(users ->
{
loadingLiveData.setValue(false);
usersLiveData.setValue(users);
});
// 你也可以直接订阅 Observable,并手动设置 LiveData
/*
model.getUsers()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
users -> {
loadingLiveData.setValue(false);
usersLiveData.setValue(users);
},
error -> {
loadingLiveData.setValue(false);
errorLiveData.setValue(error.getMessage());
}
);
*/
}
@Override
protected void onCleared() {
super.onCleared();
// ViewModel 被销毁时,LiveDataReactiveStreams 会自动取消订阅
// 如果是手动 subscribe,需要在此处管理 Disposable
}
}
// 3. View 层 (Activity/Fragment)
public class UserActivity
extends AppCompatActivity {
private UserViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user);
viewModel = new ViewModelProvider(this).get(UserViewModel.class)
;
// 观察数据变化
viewModel.getUsers().observe(this, users ->
{
// 更新 UI,例如设置 Adapter
});
viewModel.isLoading().observe(this, isLoading ->
{
if (isLoading) {
// 显示加载框
} else {
// 隐藏加载框
}
});
viewModel.getError().observe(this, errorMsg ->
{
if (errorMsg != null) {
// 显示错误信息
}
});
// 触发加载
viewModel.loadUsers();
}
}
  • 优势:
    解耦更彻底: View 通过观察数据变化来更新,无需主动调用方法。
    生命周期感知: LiveData 和 StateFlow 能自动感知 Activity/Fragment 的生命周期,避免在非活跃状态下更新 UI。
    数据驱动 UI: UI 的状态完全由数据决定,逻辑更清晰。
  • 挑战:
    桥接成本: 需要将 RxJava 流转换为 LiveData 或 StateFlow,增加了复杂性。
    学习曲线: 需要同时理解 MVVM、数据绑定和 RxJava。

1.3. RxBus (事件总线)

利用 PublishSubject 或 BehaviorSubject 实现组件间的解耦通信。

public class RxBus {
private final PublishSubject bus = PublishSubject.create();
public void post(Object event) {
bus.onNext(event);
}
public  Observable toObservable(Class eventType) {
return bus.ofType(eventType);
}
}
// 发送事件
RxBus.getInstance().post(new UserLoginEvent("Alice"));
// 接收事件
RxBus.getInstance().toObservable(UserLoginEvent.class)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(event -> {
// 更新 UI
});

1.4. 错误处理与资源管理

• 全局错误处理: 使用 RxJavaPlugins.setErrorHandler(…) 设置全局错误处理器。
• CompositeDisposable: 管理多个 Disposable,在 Activity/Fragment 销毁时统一取消,避免内存泄漏。

private CompositeDisposable disposables = new CompositeDisposable();
// 添加订阅
disposables.add(apiService.getData().subscribe(...));
// 在 onDestroy 中清理
@Override
protected void onDestroy() {
super
.onDestroy();
disposables.clear(); // 取消所有订阅
}

1.5 复杂任务编排

RxJava 通过组合操作符(如 zip、merge、concat、combineLatest)实现多任务并行或串行执行,并合并结果。以下是典型场景与实现:

1.5.1 并行任务合并(zip 操作符)

场景:同时发起多个网络请求(如获取用户信息和订单列表),待所有请求完成后统一处理结果。

Observable<
User> userObservable = api.getUser();
Observable<
OrderList> orderObservable = api.getOrders();
Observable.zip(userObservable, orderObservable,
(user, orders) ->
new UserOrderResult(user, orders))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
result ->
showUserOrders(result),
error ->
handleError(error)
);

关键点:

zip 按顺序组合多个 Observable 的数据,生成新的数据项。
发射数据量以最慢的 Observable 为准,超出的数据会被丢弃。

1.5.2. 顺序任务合并(concat 操作符)

场景:依次执行多个任务(如先登录再获取数据),前一个任务失败则终止后续任务。
实现:

Observable.concat(
api.login(),
api.getData()
).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
data ->
processData(data),
error ->
handleError(error)
);

关键点:

concat 严格按顺序执行,前一个 Observable 完成后才会订阅下一个。
适合需要严格依赖关系的任务链。

1.5.3. 动态数据流合并(merge 操作符)

场景:合并多个动态数据源(如实时股票价格和用户操作日志),不保证顺序。
实现:

Observable<
StockPrice> priceObservable = api.getStockPrices();
Observable<
UserAction> actionObservable = api.getUserActions();
Observable.merge(priceObservable, actionObservable)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
event ->
logEvent(event),
error ->
handleError(error)
);

关键点:

merge 合并多个 Observable 的数据,按时间顺序发射,可能交错。
适合无严格顺序要求的实时数据流。

1.5.4. 最新数据合并(combineLatest 操作符)

场景:当两个输入框内容变化时,实时验证表单(如密码和确认密码是否一致)。
实现:

Observable<
String> passwordObservable = RxTextView.textChanges(passwordEditText)
.skipInitialValue()
.map(CharSequence::toString);
Observable<
String> confirmPasswordObservable = RxTextView.textChanges(confirmPasswordEditText)
.skipInitialValue()
.map(CharSequence::toString);
Observable.combineLatest(
passwordObservable,
confirmPasswordObservable,
(password, confirmPassword) -> password.equals(confirmPassword)
).subscribe(
isValid ->
showValidationResult(isValid),
error ->
handleError(error)
);

关键点

combineLatest 在任意一个源 Observable 发射新数据时,组合所有源的最新数据。
适合需要基于多个输入实时计算的场景。

1.6 UI 事件处理

1.6.1 防抖优化搜索输入:

RxView.clicks(submitButton)
.throttleFirst(1000, TimeUnit.MILLISECONDS) // 1秒内仅允许一次点击
.flatMap(voidEvent -> api.submitData(data))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
success ->
showSuccess(),
error ->
handleError(error)
);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/906716.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

模型选择与配置说明

模型选择与配置说明(Detection / Recognition / Classification) 本文系统说明本项目在“检测(det)/识别(rec)/分类(cls)”三条子任务上的模型选择思路、备选方案对比、输入尺寸与性能取舍、部署格式(ONNX/MNN)、以及在 GUI 与代码层面的配置方式。目标是让读者理解“…

002_文本分类任务的问答

1、下面代码中,random_state作为随机种子作用是什么? train_x, valid_x, train_y, valid_y = model_selection.train_test_split(trainDF[text], trainDF[label], test_size=0.25, random_state=42)这段代码的作用是随机把数据分为两个部分 计算机的“随机数”其实是 伪随机数…

车牌识别

车牌识别方案对比与实现总结(GUI 三方法:lock / test / rec2) 本文面向实际工程应用,系统梳理当前 GUI 集成的三种车牌识别方法(lock、test、rec2)的技术亮点、设计思路、模型选择、实现过程与关键代码,帮助快速理解与持续优化。目标是:在统一界面中,对比“传统候选+文…

告别人工标注瓶颈!Reward-RAG:用 CriticGPT 打造更懂人类偏好的检索模型

Reward-RAG: Enhancing RAG with Reward Driven Supervision 全文摘要 本文介绍了一种名为Reward-RAG的新方法,旨在通过奖励驱动监督增强Retrieval-Augmented Generation(RAG)模型。与以往的RAG方法不同,该方法使用了CriticGPT训练了一个专门的奖励模型,并利用该模型生成合…

Latex 中百分号怎么打

Latex 中百分号怎么打 由于 % 被用作注释符,所以前面 + \ 进行转义 \(\frac{285.5}{1-2.7\%}\)

文件上传-条件竞争绕过

条件竞争原理: 条件竞争的逻辑是代码逻辑问题:当我们文件上传到服务器时,先对文件进行保存,然后对文件的后缀名进行判断,符合白名单的保存,不符合就删除,但在删除之前,有另一个对服务器发起的请求,要访问这个文件,那么就可能造成文件被读取和访问。这就是条件竞争。 …

9.17 CSP-S模拟23/多校A层冲刺NOIP2024模拟赛19 改题记录

HZOJ 写在前面 连着三天吃三坨。本来想着今天大凶忌参加模拟赛然后没模拟赛挺好的,然后7:57临时通知加场,难道这就是大凶?好吧打就打吧,没想到真差点爆零。粗看没一道题可做怀疑自己的水平了然后赛后猛然醒悟是自己蠢如猪。其实这篇前面应该还有两篇,但是奈何这套改完得比…

Java基本语法

Java基本语法Day04 今天内容大部分在复习运算符的内容和分支语句练习题 复习运算符: 运算符:+ - + - * / % (前)++ (后)++ (前)-- (后)-- + 算术运算符: 【特别说明的】 1.//(前)++ :先自增1,后运算 //(后)++ :先运算,后自增1 2.//(前)-- :先自减1,后运算 //(后)-- :先运算,…

在AI技术快速实现创想的时代,挖掘前端学习新需求成为关键——某知名编程教育平台需求洞察

本文分析了一个包含50个前端项目的编程学习资源,涵盖交互设计、动画效果和实用工具等多种类型,通过用户反馈发现了界面优化、功能扩展和教学改进等方面的潜在需求。a.内容描述 该项目是一个包含50个独立前端项目的编程学习资源,核心功能定位在于通过实际项目练习帮助开发者掌…

IvorySQL 与 deepin 完成兼容性认证,共创开源生态新篇章

近日,IvorySQL 与 deepin 操作系统成功完成了兼容性适配认证。这一里程碑式的成就标志着 IvorySQL 在国产操作系统生态中的进一步深化,为用户提供更稳定、高效的数据库解决方案。deepin 简介 深度操作系统 deepin 是一款以“简洁、美观、易用”著称的国产 Linux 发行版,拥有…

在 Nginx 上搭建静态站点

1、新建站点的配置文件 vi /etc/nginx/conf.d/www.xxx.com.conf2、写入如下内容: server {listen 80;#listen [::]:80;server_name www.xxx.com; # 这里可以写你的域名,或者 _ 表示匹配所有 root /var/www/www.xxx.com; # 你的静态文件目录 index index.html index.htm;locat…

kylin SP3安装mysql 8.4.5

环境:OS:kylin SP3mysql:8.4.5 glibc2.17,建议安装glibc.2.28版本 查看系统glibc版本[root@localhost ~]# ldd --versionldd (GNU libc) 2.28Copyright (C) 2018 自由软件基金会。这是一个自由软件;请见源代码的授权条款。本软件不含任何没有担保;甚至不保证适销性或者适合某…

Unity中是否可以禁用GC

1)Unity中可以禁用GC吗2)项目是URP管线,渲染模块CPU耗时高,经排查主要是Batches数过高,应怎样进一步排查和优化渲染批次这是第445篇UWA技术知识分享的推送,精选了UWA社区的热门话题,涵盖了UWA问答、社区帖子等技术知识点,助力大家更全面地掌握和学习。 UWA社区主页:co…

经典SQL语句大全

经典SQL语句大全一、基础1、说明:创建数据库CREATE DATABASE database-name2、说明:删除数据库drop database dbname3、说明:备份sql server--- 创建 备份数据的 deviceUSE masterEXEC sp_addumpdevice disk, testBack, c:\mssql7backup\MyNwind_1.dat--- 开始 备份BACKUP D…

Rhinoceros 8.23.25251.13001 犀牛3D建模

描述 Rhinoceros 是由美国Robert McNeel公司最新出品的专业强大的3D建模软件。软件以集百家之长为一体的发展教育理念,拥有NURBS的优秀传统建模教学方法,也有一个网格进行建模插件T-Spline,使建模方式方法有了更多的挑选,然后能创建出更传神、生动的造型。能输入和输出几十…

《深入理解计算机系统》计算机系统漫游(一) - Invinc

本文记录《深入理解计算机系统》中第1章 计算机系统漫游 的一些知识点。本文记录《深入理解计算机系统》中第1章 计算机系统漫游 的一些知识点。第1章 计算机系统漫游 信息就是位+上下文 系统中所有的信息——包括磁盘文件、内存中的程序、内存中存放的用户数据以及网络上传送的…

从几何分离到语义理解:深度解析3D点云分割与语义分割的本质区别

在三维计算机视觉领域,3D点云作为真实世界场景的核心数据载体,其处理技术直接决定了机器感知环境的精度与效率。 其中,3D点云分割与语义分割是两类关键技术,尽管二者同属点云处理范畴,但在目标定位、信息输出与应用价值上存在本质差异。 前者专注于“几何层面的分离”,通…

欧拉筛(线性筛)算法分析

前言对于寻找素数,第一时间想到的便是二重循环暴力查找,其复杂度O(n^2),通过循环中只判断到根号n可以优化一些,不过复杂度也达不到预期。在数论的学习中,我学到了埃氏筛法,O(nloglogn)的算法,而在一些数据范围达到1e7这样的题目中,也很难让人满意,于是我便学习了欧拉筛…

2021年安徽省大数据与人工智能应用竞赛 大数据(网络赛)-高职组赛题

本套数据数据: 通过网盘分享的文件: 链接: https://pan.baidu.com/s/1ZQXoWQhCOAiGVcFOZozV1g?pwd=json 提取码: json 第二部分:大数据程序编写部分(30分) 任务一、数据抽取转化部分(5分) 1、使用sqoop将MySQL的sakila数据库中的city表中city列以C开头的数据导入到hdfs…

一些写了和没写的数学!

P8114 [Cnoi2021] 六边形战士 传送 非常好玩的题! 首先你大概率看过一些“无字证明”,其中很经典的是这个: 证明:用若干个边长为 \(1\),顶角为 \(60\) 度的菱形拼成一个边长为 \(n\) 的正六边形,三个方向的菱形个数一定相等。这是一个经典的无字证明,虽然前置的说明要费…