2. MVVM架构回顾

MVVM(Model-View-ViewModel)作为Android生态中最广泛采用的架构模式,其核心价值在于职责分离与可测试性。在引入 State Hoisting 之前,先重新厘清三层的边界、数据绑定机制以及生命周期感知能力——这些是后续状态提升设计的基石。

2.1 三层职责与边界

层级 核心职责 依赖方向 关键约束
View 渲染UI、捕捉用户输入、委托事件给ViewModel 依赖ViewModel(通过观察者模式) 不含业务逻辑,不直接持有Model引用
ViewModel 管理UI状态、处理业务逻辑(通过UseCase)、协调数据流 依赖Model层(Repository/DataSource) 不持有Activity/Fragment引用,不感知View具体实现
Model 数据源封装(本地/远程/缓存)、数据转换、实体定义 无向上依赖 纯数据层,不包含UI逻辑,可独立于Android框架测试

边界规则:View 永远不能直接访问 Model;ViewModel 必须通过抽象接口(如 Repository 协议)与 Model 通信。违反此规则将导致耦合扩散,破坏架构可测试性。

2.2 数据绑定机制

MVVM 的“绑定”分为两类:单向数据流(State → UI)与事件回调(UI → ViewModel)。现代 Android 推荐使用 Jetpack Compose 或 ViewBinding + LiveData/Flow 实现。

  • 单向绑定(观察):ViewModel 暴露 StateFlow 或 LiveData,View 订阅并自动更新。
  • 反向通信(事件):View 调用 ViewModel 的方法,ViewModel 更新状态后通知 View。
// ViewModel 暴露不可变状态
class TimerViewModel : ViewModel() {
    private val _seconds = MutableStateFlow(0)
    val seconds: StateFlow<Int> = _seconds.asStateFlow()

    fun startTimer() {
        viewModelScope.launch {
            while (true) {
                delay(1000)
                _seconds.value++
            }
        }
    }
}

// Compose UI 观察
@Composable
fun TimerScreen(viewModel: TimerViewModel) {
    val seconds by viewModel.seconds.collectAsState()
    Text("Elapsed: $seconds s")
}

选型权衡:DataBinding 适用于传统 XML 布局,但编译期生成代码调试困难;Compose 具备更好的组合性和状态管理,但要求全量迁移。混合使用时注意 ViewModel 状态必须统一通过 StateFlow 暴露,避免两套绑定逻辑冲突。

2.3 生命周期感知

ViewModel 天然感知 Activity/Fragment 生命周期:onCleared() 在拥有者销毁时调用,自动取消协程。LiveData 或 Flow 配合 lifecycleScope 确保观察在非活跃状态下暂停,避免内存泄漏和无效更新。

机制 生命周期感知方式 风险点
ViewModel + viewModelScope 自动跟随 ViewModelStoreOwner 销毁 小心在非 ViewModel 作用域(如 Application)中创建 ViewModel
LiveData.observe(lifecycleOwner) 仅在前台时发送更新 重复订阅、丢失粘性事件需手动处理
Flow.collect(lifecycle.repeatOnLifecycle) 生命周期恢复时重新收集 需要正确处理取消和重连,避免重复发射

常见陷阱:在 ViewModel 中直接持有 Context 或 View 实例将破坏生命周期感知,导致内存泄漏。务必通过 Application 级别的 Context(如 AndroidViewModel)或使用 SavedStateHandle 传递必要参数。

2.4 架构示意图:三层协作与数据流

MVVM 三层架构与数据流 MVVM 三层架构 & 数据绑定 / 生命周期感知 View Activity / Fragment / Compose ViewModel 状态管理 + 业务编排 Model Repository / DataSource 事件(点击 / 输入) StateFlow / LiveData UseCase → Repository 生命周期感知 lifecycleScope / repeatOnLifecycle 数据绑定 单向状态 + 回调事件 viewModel.seconds.collectAsState()

2.5 风险点与落地检查清单

  • 过度绑定:View 中直接使用 DataBinding 表达式调用 ViewModel 方法,导致测试时需模拟整个 DataBinding 环境。应强制所有事件触发走普通函数调用。
  • 状态提升不彻底:部分状态仍保存在 View 的本地变量中,绕过 ViewModel。可使用 Lint 规则或 code review 强制所有 UI 状态来源于 ViewModel。
  • 生命周期耦合:ViewModel 中持有 Fragment 的 LifecycleOwner 引用,造成泄漏。应完全避免传递任何 View 相关对象。
  • 信任链路:ViewModel 不应信任 View 传递的原始事件,应进行输入校验;Model 层数据不应被 ViewModel 直接篡改,必须通过不可变对象映射。

验收标准:

  • 所有 UI 状态均声明为 StateFlow / LiveData,且 View 只订阅一次。
  • ViewModel 单元测试可独立运行(不依赖 Android 环境)。
  • 生命周期回放(旋转屏幕)后状态自动恢复,不丢失。