MVI 是 UI 架构演进的"终点站"之一,Jetpack Compose、Redux 都基于它的思想。它解决的是《MVVM 模式》遗留的"状态分散、数据流难追踪"问题。这篇把 MVI 的核心——单向数据流、不可变状态——讲清楚。
一、解决什么问题:MVVM 的状态分散
MVVM 的双向绑定好用,但有个隐患:状态散落各处,数据流不透明。
- 一个界面可能有多个 LiveData、多个双向绑定
- 状态怎么变的、谁改的、什么时候改的,追踪困难
- 复杂界面下,多个状态互相影响,出现"改 A 意外影响了 B"的 bug,排查半天
MVI 的思路是反过来的:状态只有一个、数据只能单向流——把"状态"集中成一个不可变对象,任何变化都走同一条路,有迹可循。
二、MVI 三部分
| 部分 | 职责 | 特点 |
|---|---|---|
| Model | 界面状态(State) | 不可变(immutable) |
| View | 渲染状态 + 发送 Intent | 只渲染,不持有业务状态 |
| Intent | 用户意图(事件) | 用户干了啥,就发啥 |
关键区别:MVI 里的 Model 不是"数据+业务",而是"界面状态"(当前加载中?列表数据是啥?)。View 渲染它,用户操作发 Intent,Intent 处理后产生新的 State,View 再渲染。
三、单向数据流(核心)
MVI 的核心是数据只朝一个方向流,没有回头路:
View 发送 Intent → 处理(Reducer)→ 产生新 State → View 渲染新 State
└──────────────── 单向,无环 ──────────────────┘用 Compose 看最直观:
kotlin
// ① State:不可变状态(data class,每次产生新的)
data class UiState(
val loading: Boolean = false,
val users: List<User> = emptyList(),
val error: String? = null
)
// ② Intent:用户意图(sealed class,枚举所有用户操作)
sealed class UserIntent {
data class LoadUsers(val deptId: Int) : UserIntent()
object Refresh : UserIntent()
}
// ③ 处理:Intent → 新 State(纯函数,输入确定输出就确定)
fun reduce(state: UiState, intent: UserIntent): UiState = when (intent) {
is UserIntent.LoadUsers -> state.copy(loading = true)
UserIntent.Refresh -> state.copy(loading = true)
}
// ④ View 渲染 State,发 Intent
// 用户点"刷新" → 发 Refresh Intent → reduce 产生 loading=true 的新 State → View 显示加载中四个关键点:
- 单向:
Intent → State → View,永远一个方向,不会像双向绑定那样来回 - 不可变 State:
state.copy(loading = true)产生新对象,不改旧对象——旧状态永远可追溯 - 状态集中:一个界面的状态就是一个
UiState,不再散落成多个 LiveData - 可追溯:任何时刻的状态,都是"初始 State + 一串 Intent"一步步 reduce 出来的,debug 时能完整回放
四、MVI vs MVVM
| 维度 | MVVM | MVI |
|---|---|---|
| 数据流 | 双向绑定(来回) | 单向数据流(一个方向) |
| 状态 | 分散(多个 LiveData) | 集中(单一不可变 State) |
| 状态变化 | 难追踪 | 可追溯(Intent 回放) |
| 典型 | Vue、DataBinding | Compose、Redux、Flutter |
一句话:MVVM 图省事(双向自动同步),MVI 图可控(单向可追溯)。
五、落地
- Jetpack Compose:
MutableStateFlow<UiState>+sealed class Intent,状态提升(State Hoisting) - Redux:
Store → Action → Reducer → State,MVI 就是 Redux 思想在 UI 的落地 - Flutter:
Bloc模式(Event → Bloc → State)
小结
- MVI 解决 MVVM"状态分散、难追踪"的问题,核心是单向数据流 + 不可变状态
- 三部分:State(不可变状态)+ View(渲染 + 发 Intent)+ Intent(用户意图)
- 数据流:View 发 Intent → reduce 产生新 State → View 渲染,单向无环、可回放
- 落地:Compose、Redux、Flutter Bloc
MVI 是目前 UI 架构里"可维护性"最强的一档,代价是样板代码比 MVVM 多——简单界面 MVVM 够用,复杂状态多的界面用 MVI 更省心。想了解这三个模式的源头,回头看《MVC 模式》。
