Skip to content

Android 架构核心原则:单一数据源(SSOT)与单向数据流(UDF)实战指南


🧭 一、为什么需要单一数据源(SSOT)

在传统的 Android 开发中,我们常常将业务逻辑、UI 状态、网络请求等全部代码都堆在一个 ActivityFragment 里。 这样做虽然一开始看似简单,但随着项目变大,代码将变得臃肿、难以维护,最典型的问题就是 —— 状态不一致

常见问题

  • 多处保存同一份数据(UI 层、Repository 层、网络层各自维护一份)
  • 数据更新不同步,UI 显示过期信息
  • 测试困难、耦合严重
  • 生命周期变化导致状态丢失或内存泄漏

解决思路

我们需要一个 唯一可信的数据源(Single Source of Truth, SSOT),所有数据都应从这里获取、更新、暴露。 它相当于应用中“数据的唯一真相”。


🔑 二、什么是单一数据源(SSOT)

定义: 单一数据源是指对某类数据的唯一权威持有者。只有这个源能修改或转换数据,其他模块只能以“只读”的形式访问。

在 Android 架构中,SSOT 通常由 RepositoryViewModel 扮演:

  • 内部持有可变数据(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 + ComposeStateFlowUiState 是界面状态的 SSOT
MVI 架构State 是单一数据源,Action 修改它
Clean Architecture每个实体的 Repository 是该领域的 SSOT

🧩 六、常见问题与优化建议

问题原因解决方案
多处保存状态Repository 与 ViewModel 各自维护让 Repository 成为唯一持有者
状态可变被外部修改暴露了 MutableStateFlow只暴露只读 StateFlow
一次性事件混入状态流Toast、导航事件与状态流混用SharedFlowChannel 处理一次性事件
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 架构的核心基石

随便写写的,喜欢就好。 使用VitePress构建