MVVM 迁移到 MVI:复杂页面状态如何收敛

简介: 本文探讨Android复杂页面(含筛选、分页、刷新、错误恢复)从MVVM向MVI迁移的实践:聚焦状态收敛、单向数据流设计、请求代次防竞态、分页逻辑集中管控,并倡导渐进式迁移——不为换架构而换,只为让状态可解释、可测试、可还原。(239字)

[Android 从零到一] MVVM 迁移到 MVI:复杂页面状态如何收敛

当页面同时包含筛选、分页、刷新和错误恢复时,单纯增加 ViewModel 字段往往会让状态彼此打架。本文用一个列表页面说明何时需要从 MVVM 的多状态更新迁移到单向数据流,以及如何控制迁移范围。

问题不是架构名字,而是状态组合

假设一个商品列表页有关键词、分类、排序、分页、加载状态和错误提示。若 ViewModel 分别暴露 items、loading、error、page、filter,页面可能收到互相矛盾的组合:旧列表配新筛选、加载结束却仍显示旧错误,或刷新和加载更多同时发生。

MVVM 本身并不要求状态散落。问题通常出在状态没有明确的单一所有者,以及异步结果没有验证其所属请求。迁移到 MVI 的价值,是把用户意图、状态转移和渲染结果串成可追踪的单向链路,而不是为了给类改名。

先定义页面状态

将页面可见信息聚合成一个不可变状态。筛选条件和列表数据属于稳定状态,正在执行的动作属于加载状态,错误则作为可恢复的界面结果表达。

data class ProductUiState(
    val query: String = "",
    val categoryId: String? = null,
    val sort: Sort = Sort.Recommended,
    val items: List<Product> = emptyList(),
    val nextPage: Int = 1,
    val isRefreshing: Boolean = false,
    val isAppending: Boolean = false,
    val endReached: Boolean = false,
    val message: String? = null,
)

sealed interface ProductIntent {
    data class QueryChanged(val value: String) : ProductIntent
    data class CategorySelected(val id: String?) : ProductIntent
    data class SortSelected(val sort: Sort) : ProductIntent
    data object Refresh : ProductIntent
    data object LoadNextPage : ProductIntent
    data object MessageConsumed : ProductIntent
}

UI 只订阅一个 StateFlow<ProductUiState> 并发送意图。状态更新始终基于旧状态生成新值,不从 View 层反向修改数据。一次性提示可以用有明确消费动作的状态字段,也可以用独立事件流;选择哪种方式要看是否需要在配置变更或进程重建后重放。

用请求代次处理竞态

仅靠单向数据流不能自动解决网络竞态。用户快速切换筛选时,较早发出的请求可能较晚返回并覆盖新结果。让每次查询变更取消前一任务,并在更新状态时确保任务仍然有效:

private var searchJob: Job? = null

private fun search(query: String, categoryId: String?, sort: Sort) {
    searchJob?.cancel()
    searchJob = viewModelScope.launch {
        _uiState.update { it.copy(isRefreshing = true, message = null) }
        runCatching { repository.search(query, categoryId, sort) }
            .onSuccess { result ->
                _uiState.update {
                    it.copy(
                        items = result.items,
                        nextPage = result.nextPage,
                        endReached = result.endReached,
                        isRefreshing = false,
                        isAppending = false,
                    )
                }
            }
            .onFailure { error ->
                if (error is CancellationException) throw error
                _uiState.update {
                    it.copy(isRefreshing = false, isAppending = false, message = error.toUserMessage())
                }
            }
    }
}

生产代码还应确保 Repository 的取消能够传递到底层请求;如果数据源不支持取消,可给每次查询分配递增序号,只允许最新序号提交结果。不要吞掉 CancellationException,否则上游取消可能被错误地当作普通失败。

将分页规则集中在状态所有者

LoadNextPage 应在正在刷新、正在追加或已经到末页时忽略。追加成功后合并数据并推进页码;失败时保留已有列表,避免一次网络错误清空用户正在看的内容。相同商品是否去重、刷新期间旧数据是否继续展示,也应作为明确的产品规则,而非散落在 Adapter 中的补丁。

这一点体现了 MVI 的实际收益:状态转移可以围绕意图集中实现并单测,而不必在多个回调中猜测当前组合是否合理。但如果页面只有简单加载和空态,使用一个清晰的 UiState 的 MVVM 也完全足够,不需要额外引入 reducer 框架。

渐进迁移,不重写整个模块

迁移可分成三步:先把 ViewModel 的多个可变字段收敛成一个不可变 UI 状态;再把页面回调整理成有限的意图入口;最后把重复的异步转移抽成私有函数或 reducer,并补上并发与失败测试。Repository、导航和现有列表组件通常无需同时改造。

测试优先覆盖状态转移边界:快速变更筛选时旧请求不能覆盖新结果;刷新失败时旧列表仍然存在;追加失败不会推进页码;末页之后不会继续请求;重复点击加载不会并发发出重复任务。使用协程测试工具控制调度和请求完成顺序,比依赖真实网络更稳定。

什么时候值得采用

当多个异步动作会互相影响、状态组合难以推理、线上问题无法还原具体转移路径时,单向数据流通常能降低维护成本。若页面状态简单、团队已经有成熟且一致的 MVVM 约定,先完善状态建模和并发控制即可。架构选择的判断标准应是状态变化能否被解释、测试和恢复,而不是模式名称是否流行。

相关文章
|
23天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
9321 29
|
9天前
|
存储 人工智能 并行计算
大模型本地部署终端选型方法论:以 Qwen3.8-27B 为例的四档分层完整流程
本文提出一套大模型本地部署终端选型方法论:定约束、定档位、定框架、定参数四步决策法,配合入门、主力、质量、无损四档分层模型。以 Qwen3.8-27B 实测数据为例,逐环节解读显存、带宽、存储、散热、系统、预算等要素,给出面向不同预算的优选方案、决策自查清单与市场观察框架。文末前瞻 AI 笔记本的 CPU+GPU 与统一内存两条路线,论证四步决策法在新品类上的延续性。
|
21天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
2447 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
7天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型深度拆解:原理解读、实战测评与保姆级落地教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为**System One Model(系统一模型)**,对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
615 1
|
2天前
|
存储 缓存 测试技术
QWT 3D 模块重构记:旧渲染管线改进介绍及实时瀑布图示例
QWT 3D模块完成重大重构:解耦窗口与图形(Plot+Item),摒弃OpenGL 1.x固定管线,迁至Core Profile着色器渲染;支持多图元同场景叠加,并以实时3D瀑布图验证——滑动窗口、固定色域、64帧×256点达61fps。兼容Qt 5.12+/6,LGPL协议。
154 120
|
10天前
|
人工智能 Linux Windows
千问办公(QwenWork)官网入口:其实有2个,一个是网页端千问办公,一个是介绍指南页面
千问办公(QwenWork)是阿里云推出的AI智能办公平台,支持网页端直接使用及Windows/Mac/Linux客户端下载。提供PPT生成、财报分析、网页搭建等AI功能,个人版免费,企业版198元/席/月。详情见官网qwenwork.cn或阿里云产品页。
1174 0
千问办公(QwenWork)官网入口:其实有2个,一个是网页端千问办公,一个是介绍指南页面
|
14天前
|
Windows
【保姆级】.NET Framework 3.5离线安装教程(2026最新实测)
.NET Framework 3.5是微软经典运行库,专为兼容老软件、老游戏设计。Win7自带,Win10/11需手动启用(在线或离线安装)。它与4.x系列并存不冲突,缺它会导致“找不到组件”报错。本文详解两种安装方式及常见问题解决,小白也能轻松上手。(239字)
1780 0
|
4天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型全解析:核心原理拆解,性能实战测评与落地部署教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为System One Model(系统一模型),对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
391 0