Android 架构系列之MVVM 和 MVI 算架构吗?

1 阅读12分钟

本文是 Android 架构系列的讨论,聚焦一个很多人没想清楚的问题:MVVM 和 MVI 到底算不算架构?


前言

在 Android 开发里,MVVM 和 MVI 几乎是绕不开的两个词。

很多人刚开始接触架构时,都会冒出同一个疑问:

MVVM 和 MVI 到底算不算架构?

有人说算,因为项目里确实是"按 MVVM 写的";有人说不算,因为它们只管 UI,不管网络、数据库、缓存和业务逻辑。

这个问题看起来像在抠概念,但实际上非常重要。

没搞清楚 MVVM、MVI 的边界,很容易产生一种 架构焦虑

  • 项目用了 ViewModel,是不是就算有架构了?
  • 用了 MVI,是不是就比 MVVM 更高级?
  • Repository、UseCase、DataSource 又应该放在哪里?
  • 为什么用了 MVVM,代码最后还是乱成一团?好像这个问题很普遍

本篇文章就直接说清楚:

MVVM 和 MVI 算架构,但它们更准确地说是"表示层架构模式",不能代表整个 App 的完整架构。Google 推荐的Android 现代架构的整体骨架,本质上是 Clean Architecture 的 Android 版本。


一、结论先行:MVVM 和 MVI 算架构吗?

算。

但要加一个限定词:

它们是"微观架构",也就是 UI 层 / 表示层的架构模式。

它们主要解决的是 UI 层内部的职责划分问题:

  • Activity / Fragment 里代码太多
  • UI 展示逻辑和业务逻辑混在一起
  • 用户操作、状态变化、页面刷新逻辑混乱
  • 页面状态难以追踪和测试

从"分配职责"和"约束代码组织方式"的角度看,MVVM 和 MVI 当然属于架构模式。

但它们并不能解决整个 App 的所有架构问题。比如下面这些,MVVM 和 MVI 本身不会告诉你答案:

  • 网络请求应该写在哪里?
  • 数据库和接口数据如何配合?
  • 缓存策略放在哪一层?
  • 复杂业务逻辑写在 ViewModel 还是 UseCase?
  • 多个页面复用同一段业务逻辑怎么办?

所以更准确的说法是:

MVVM 和 MVI 是 UI 层的架构模式,不是完整应用的系统架构。


二、为什么说 MVVM 和 MVI "算架构"?

架构的本质,不是为了显得高级,也不是为了炫耀,更不是为了物理分层。

架构的核心价值只有两个:解决混乱,分配职责。

在 Android 早期开发中,Activity 和 Fragment 很容易变成"万能类"。一个 Activity 里可能同时包含:

  • findViewById / ViewBinding、点击事件
  • 网络请求、数据库查询、数据格式转换
  • 页面状态控制、Loading / Error / Empty 展示
  • 跳转逻辑、埋点逻辑、权限处理……

最后 Activity 越写越大,几千行代码并不罕见。这就是典型的 God Object 问题

MVVM 和 MVI 的出现,本质上就是为了拆解这个"上帝类"。


三、MVVM 解决了什么问题?

MVVM 全称:Model - View - ViewModel

在 Android 中,常见的对应关系是:

角色Android 实现
ViewActivity / Fragment / Compose UI
ViewModelJetpack ViewModel
ModelRepository / 数据模型 / 数据来源

核心思路:View 只负责展示和交互,ViewModel 负责管理页面状态和业务调用。

一个典型的 MVVM 数据流:

image.png

这里只是demo,实际上异常不该在ViewModel 处理。请见: 为什么我不在 Android ViewModel 中直接处理异常

View 层只负责观察状态:

image.png

Activity / Fragment 不再承担过多职责,UI 和逻辑解耦,状态有地方存放,代码也更容易测试和维护。


四、MVI 解决了什么问题?

MVI 全称:Model - View - Intent

它比 MVVM 更强调两件事:

单向数据流 + 唯一可信状态源

在 MVI 中,页面被抽象为三个核心概念:

  • Intent:用户意图,比如点击按钮、输入文本、下拉刷新
  • State:页面完整状态,比如 Loading、Content、Error
  • Reducer:根据旧状态和新事件生成新状态

MVI 的典型数据流:

image.png

MVI 的核心主张:UI 不应该到处维护零散状态,而是由一个完整的 State 来驱动。

MVVM 风格,状态往往是分散的:

val isLoading     = MutableStateFlow(false)
val username      = MutableStateFlow("")
val password      = MutableStateFlow("")
val errorMessage  = MutableStateFlow<String?>(null)

MVI 用一个聚合 State 来描述整个页面:

image.png

用户行为被显式建模为 Intent,ViewModel 统一处理:

image.png

MVI 的优势:✅ 用户行为清晰,状态变化可追踪,UI 渲染更稳定,更适合复杂页面和 Jetpack Compose。


五、为什么说 MVVM 和 MVI 又"不够完整"?

如果一个项目只用了 MVVM,能不能说它已经有完整架构了?

不太严谨。

如果所有逻辑都塞进 ViewModel,Activity / Fragment 确实变瘦了,但 ViewModel 又会变成新的"上帝类":

image.png

这种代码表面上是 MVVM,实际上只是把混乱从 Activity 搬到了 ViewModel。

MVVM 和 MVI 只能说明 UI 层怎么组织,不能代表整个系统架构已经清晰。

那宏观上,Android 项目应该用什么架构来兜底?


六、Android 现代架构的本质:Clean Architecture 的 Android 版

说清楚这件事之前,先退一步看 Clean Architecture 本身。

6.1 什么是 Clean Architecture?

Clean Architecture 是 Robert C. Martin(Uncle Bob)在 2012 年提出的架构思想,核心是一张著名的同心圆图:

CleanArchitecture.jpg

Clean Architecture 有一条铁律,叫做 依赖规则(The Dependency Rule)

源码中的依赖关系,只能由外层指向内层,永远不能反过来。

内层对外层一无所知。业务核心(Entities、Use Cases)不依赖任何 UI 框架、数据库、网络库——只依赖抽象接口。

这条规则带来的好处非常直接:

  • ✅ 业务逻辑可独立测试,不需要 Android 环境
  • ✅ 随时可替换外层技术栈而不动业务代码
  • ✅ 每一层只关心自己的职责

6.2 Google 的分层架构就是 Clean Architecture 的 Android 版

Google 官方 Android Architecture Guide 推荐的分层架构:

image.png

对照 Clean Architecture,这三层的映射关系非常清晰:

Clean ArchitectureAndroid 分层架构具体实现
Frameworks & Drivers(UI 侧)UI LayerActivity, Fragment, Compose, ViewModel
Application Business RulesDomain LayerUseCase
Enterprise Business RulesDomain Layer 中的 Entity核心领域模型
Interface AdaptersRepository 接口 + Mapper连接 Domain 和 Data 的桥梁
Frameworks & Drivers(数据侧)Data LayerRetrofit, Room, DataStore

依赖方向完全吻合 Clean Architecture 的依赖规则:

image.png

6.3 依赖规则在 Android 里怎么落地?

最典型的体现就是 Repository 接口与实现的分离

Domain Layer 定义接口(内层,稳定):

image.png

Data Layer 提供具体实现(外层,可替换):

image.png

UseCase 依赖接口而不是具体实现:

image.png

这就是 依赖倒置原则(DIP) 在 Android 里最直接的体现:内层定义规则(接口),外层负责实现,依赖方向永远朝内。


七、三层架构详解

UI Layer:MVVM / MVI 的主场

UI Layer 负责界面展示和用户交互,是 MVVM / MVI 发挥作用的地方

image.png

ViewModel 是 UI Layer 和 Domain Layer 的边界。它不应该直接持有 Retrofit、Room 之类的框架类,更不应该写业务规则——那是 UseCase 的职责。

Domain Layer:业务逻辑的家

Domain Layer 是整个架构的核心,对应 Clean Architecture 的 Application Business Rules。

它有一个非常重要的特征:

纯 Kotlin,零 Android 依赖。

UseCase 是 Domain Layer 的基本单元,每个 UseCase 只做一件事

image.png

它只做一件事,并不代表只有一个方法,请见: Android Clean Architecture 中 Use Case 只能有一个方法吗?

ViewModel 只需调用 UseCase,无需关心业务细节:

image.png

Data Layer:数据的唯一来源

Data Layer 负责屏蔽数据来源,对上层只暴露干净的领域模型。

image.png

Mapper 的存在至关重要,它保证了各层数据模型的独立性:

image.png

八、一张图说清楚整体关系

Activity Fragment to-2026-05-16-033738.png

九、正确描述你的项目架构

如果你想准确描述一个 Android 项目的架构,不建议只说:

我们项目用的是 MVVM 架构。

这句话没错,但不完整,而且会让人以为你的架构里没有 Data Layer 和 Domain Layer。

更准确的表达应该是:

整体采用 Clean Architecture 思想,按 Google 推荐落地为 UI / Domain / Data 三层架构;在 UI Layer 内部,使用 MVVM / MVI 进行页面状态管理。

这句话说清楚了三件事:

  • 宏观骨架来自 Clean Architecture(明确架构思想来源)
  • 分层方式采用 Google 官方推荐(说明具体落地方案)
  • UI 层内部用 MVVM / MVI(说明表示层组织方式)

十、MVVM vs MVI:怎么选?

不要为了"更高级"而选 MVI,也不要因为 MVVM 简单就觉得它落后。

架构选型应该看项目复杂度、团队熟悉度和维护成本。

选 MVVM 的场景

场景说明
中小型项目引入成本低,上手快
页面状态不复杂无需管理大量联动状态
团队水平参差社区资料丰富,易于学习
快速迭代业务写法灵活,模板代码少

⚠️ MVVM 常见问题:页面复杂后,多个独立的 StateFlow 之间关系容易失控。

选 MVI 的场景

场景说明
播放器、编辑器类页面状态多且联动复杂
复杂表单、多步骤流程需要严格追踪状态变化
强交互 Compose 页面与 Compose 的 State 驱动模型天然契合
状态回放 / 调试要求高单向数据流更易定位问题

MVI 明显成本:模板代码更多,简单页面会显得偏重。

实用建议

整体使用 Clean Architecture 分层,普通页面用 MVVM,复杂页面用 MVI。两者不互斥。

页面类型建议方案
设置页、用户信息页MVVM
首页 Feed 流MVVM 或轻量 MVI
播放器页、发布编辑器MVI
复杂表单、多选管理MVI

十一、总结

回到标题的问题:MVVM 和 MVI 算架构吗?

算,但它们主要是 UI 层的架构模式。

它们能解决 Activity / Fragment 臃肿、UI 状态混乱、界面逻辑难维护等问题,但不能单独解决整个 App 的架构问题。

Android 现代架构的宏观骨架,本质上是 Clean Architecture 的 Android 落地版:

概念Clean ArchitectureAndroid 落地
核心思想依赖规则:外层依赖内层UI → Domain ← Data
业务核心Entities + Use CasesDomain Layer(纯 Kotlin)
适配层Interface AdaptersRepository 接口 + Mapper
框架层Frameworks & DriversUI 框架 + Retrofit + Room
表示层模式不限定MVVM 或 MVI

最后:

MVVM / MVI 解决的是 UI 层的问题,Clean Architecture 解决的是整个系统的问题。两者不是竞争关系,而是不同粒度上的架构决策。搞清楚这一点,很重要。

架构 系列文章索引(持续更新)

分类文章
Clean Architecture从送外卖看 Android Clean 架构
Clean Architecture用一个小 Demo 带你入门 Clean Architecture
Clean ArchitectureUseCase 的「守边计划」:不启动协程,不切线程,但可以并发
Clean Architecture为什么 UI 不建议直接访问 Repository
Clean Architecture是时候告别 Manager 了
协程协程中的三大反模式
协程ViewModel 不该处理异常
协程协程时代,出现 ReentrantLock 就是架构警报
Android 现代化Handler 在协程时代的退场
Android 现代化内存泄漏的现代解法
Android 现代化为什么 Google 不再推荐 SharedPreferences
工具与方法论先用 IntelliJ 学透 Kotlin 协程,再进入 Android
工具与方法论suspend 与 Flow 的接口语义规范