Android 架构核心原则:单一数据源(SSOT)与单向数据流(UDF)实战指南
🧭 一、为什么需要单一数据源(SSOT)
在传统的 Android 开发中,我们常常将业务逻辑、UI 状态、网络请求等全部代码都堆在一个 Activity
或 Fragment
里。 这样做虽然一开始看似简单,但随着项目变大,代码将变得臃肿、难以维护,最典型的问题就是 —— 状态不一致。
常见问题
- 多处保存同一份数据(UI 层、Repository 层、网络层各自维护一份)
- 数据更新不同步,UI 显示过期信息
- 测试困难、耦合严重
- 生命周期变化导致状态丢失或内存泄漏
解决思路
我们需要一个 唯一可信的数据源(Single Source of Truth, SSOT),所有数据都应从这里获取、更新、暴露。 它相当于应用中“数据的唯一真相”。
🔑 二、什么是单一数据源(SSOT)
定义: 单一数据源是指对某类数据的唯一权威持有者。只有这个源能修改或转换数据,其他模块只能以“只读”的形式访问。
在 Android 架构中,SSOT 通常由 Repository 或 ViewModel 扮演:
- 内部持有可变数据(
MutableStateFlow
/MutableLiveData
) - 对外暴露只读数据(
StateFlow
/LiveData
) - 仅通过定义好的方法或事件修改数据
优点:
- 所有更改集中管理,数据一致性更高
- 防止外部模块篡改数据
- 更容易追踪数据变动,排查问题更高效
🔄 三、单向数据流(UDF)
单向数据流(Unidirectional Data Flow,简称 UDF)与 SSOT 密切相关。 它的核心思想是:
状态单向流动(Data flows down),事件反向流动(Events flow up)。
流程示意:
用户操作 ─▶ ViewModel ─▶ Repository ─▶ 更新数据源
▲ │
└──────────── UI 订阅状态 ◀────────────┘
- 数据流向下:从数据层流向 UI 层
- 事件流向上:从 UI 层向数据层
优势:
- 数据始终保持一致性
- 状态变化可预测、可追踪
- 模块职责分明,便于调试与扩展
⚙️ 四、实战:在 Android MVVM 架构中实现 SSOT + UDF
🧱 Repository(单一数据源)
kotlin
class UserRepository {
private val _user = MutableStateFlow<User?>(null)
val user: StateFlow<User?> = _user.asStateFlow()
suspend fun fetchUser() {
val result = api.getUser()
_user.value = result
}
}
🧩 ViewModel(事件处理 + 状态桥梁)
kotlin
class UserViewModel(private val repo: UserRepository): ViewModel() {
val user = repo.user
fun onRefresh() {
viewModelScope.launch { repo.fetchUser() }
}
}
🎨 UI 层(观察状态)
kotlin
@Composable
fun UserScreen(vm: UserViewModel = viewModel()) {
val user by vm.user.collectAsState()
user?.let {
Text("Hello, ${it.name}")
} ?: CircularProgressIndicator()
}
✅ 特点:
- 唯一的数据来源是
UserRepository
- UI 层只订阅状态,不直接修改数据
- 状态变化自动触发 UI 更新
🧠 五、SSOT 在不同架构中的体现
架构层 | SSOT 扮演者 |
---|---|
Room + Repository | 数据库是离线优先应用的 SSOT |
ViewModel + Compose | StateFlow 或 UiState 是界面状态的 SSOT |
MVI 架构 | State 是单一数据源,Action 修改它 |
Clean Architecture | 每个实体的 Repository 是该领域的 SSOT |
🧩 六、常见问题与优化建议
问题 | 原因 | 解决方案 |
---|---|---|
多处保存状态 | Repository 与 ViewModel 各自维护 | 让 Repository 成为唯一持有者 |
状态可变被外部修改 | 暴露了 MutableStateFlow | 只暴露只读 StateFlow |
一次性事件混入状态流 | Toast、导航事件与状态流混用 | 用 SharedFlow 或 Channel 处理一次性事件 |
LiveData 与 Flow 混用 | 更新不一致、难追踪 | 统一使用 Flow 管理状态 |
🚀 七、SSOT 与 Clean Architecture 的结合
在 Clean Architecture 中,结构层次如下:
- Data 层:Repository + DataSource(操作数据库、网络)
- Domain 层:UseCase(业务逻辑)
- Presentation 层:ViewModel + UI(状态展示)
数据流动自下而上,状态流动自上而下。 Repository 是每个实体的 单一数据源,而 ViewModel 和 UI 只负责“观察”和“展示”。
在 Jetpack Compose + UDF 模式中,ViewModel
中的 UiState
即为局部的 SSOT,UI 是它的纯函数表现。
🧩 八、与 Redux / MVI 模式的关系
Redux 与 MVI 架构天生就是 SSOT + UDF:
Store
是唯一的数据源State
是只读的Action
是修改数据的唯一途径
而 Android 中以 ViewModel + StateFlow
为核心的架构,本质上就是 轻量级的 Redux 模式。
🧭 九、总结
概念 | 含义 | 优点 |
---|---|---|
SSOT | 单一数据源 | 数据一致性、集中管理 |
UDF | 单向数据流 | 可预测、可追踪 |
分离关注点 | UI / Logic / Data 分层 | 高内聚、低耦合 |
状态不可变 | 对外暴露只读状态 | 防止副作用与误修改 |
✅ 关键结论
- 让数据只有一个来源(Single Source of Truth)
- 状态流动单向、可预测
- UI 层只渲染数据,不持有业务逻辑
- SSOT + UDF 是现代 Android 架构的核心基石