MVVM 架构模式浅析

6 阅读5分钟

1. MVVM 核心概念

MVVM 是 Model-View-ViewModel 的缩写,它是一种数据驱动的架构模式,旨在通过观察者模式数据绑定实现视图与业务逻辑的分离。

  • Model:数据层,负责数据的获取和存储(网络、数据库、SharedPreferences 等),通常以 Repository 形式存在。
  • View:视图层,即 Activity、Fragment 或 Compose 组件,负责 UI 绘制和用户交互事件的接收。View 会观察 ViewModel 中的数据变化,并自动更新 UI。
  • ViewModel:视图模型,是 MVVM 的核心。它暴露数据流(如 LiveData、StateFlow)和处理用户动作的方法,但不持有 View 的引用,因此与 View 解耦,生命周期更长,更易于测试。

2. MVVM 的核心机制

2.1 数据驱动 UI

  • ViewModel 通过可观察的数据容器(LiveData、StateFlow、RxJava 等)将状态暴露给 View。
  • View 通过 observe() 或绑定表达式订阅这些数据,当数据变化时,UI 自动更新。

2.2 数据绑定(DataBinding / ViewBinding)

  • DataBinding 允许直接在布局文件中绑定 ViewModel 的属性,通过 @{} 语法实现自动同步。
  • 双向绑定(@={})可实现输入控件(如 EditText)的内容自动更新到 ViewModel。
  • ViewBinding 更轻量,仅用于生成绑定类,减少 findViewById,但 UI 更新仍需手动设置观察者。

2.3 生命周期感知

  • ViewModel 类本身是生命周期感知的,它会在 Activity 或 Fragment 的关联生命周期结束时自动清理资源。
  • LiveData 也是生命周期感知的,它只在观察者处于活跃状态(如 STARTED)时才会更新 UI,避免空指针和内存泄漏。

3. MVVM 的典型实现(以 Android Jetpack 为例)

3.1 组件栈

  • ViewModelandroidx.lifecycle.ViewModel
  • LiveData 或 Kotlin Flow:用于暴露数据
  • Repository:单一数据源,封装数据操作
  • DataBinding 或 Compose:用于 UI 绑定

3.2 示例代码片段

ViewModel

class UserViewModel : ViewModel() {
    private val _userName = MutableLiveData<String>()
    val userName: LiveData<String> get() = _userName

    fun loadUser() {
        // 模拟从 Repository 获取数据
        _userName.value = "John Doe"
    }
}

Activity 中使用 ViewModel 和 DataBinding

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        val viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
        binding.viewModel = viewModel
        binding.lifecycleOwner = this  // 使 LiveData 可感知生命周期
    }
}

布局文件(activity_main.xml)

<layout>
    <data>
        <variable name="viewModel" type="com.example.UserViewModel" />
    </data>
    <TextView
        android:text="@{viewModel.userName}"
        ... />
</layout>

4. MVVM 的优势

  • 解耦性高:ViewModel 不持有 View 引用,View 只观察数据,两者可独立变化。
  • 可测试性强:ViewModel 纯业务逻辑,不依赖 Android 组件,单元测试非常方便。
  • 生命周期安全:ViewModel 和 LiveData 自动管理生命周期,避免内存泄漏和空指针。
  • 减少模板代码:数据绑定自动同步 UI,减少手动更新代码。
  • 支持屏幕旋转:ViewModel 在旋转时存活,数据不丢失,无需手动保存状态。

5. 常见进阶问题

5.1 ViewModel 的生命周期是怎样的?如何保证数据在旋转时不丢失?

  • ViewModel 的生命周期从 Activity/Fragment 创建(onCreate)开始,直到 Activity 真正销毁(onDestroy)才结束。屏幕旋转时,Activity 会重新创建,但 ViewModel 仍保留在内存中,通过 ViewModelProvider 返回同一个实例,因此数据得以保持。
  • 其实现原理:ViewModelStore 存储在 Activity 的 NonConfigurationInstances 中,旋转时系统会保留该存储。

5.2 ViewModel 中可以持有 Context 吗?

  • 尽量避免直接持有 Context,否则可能引起内存泄漏。如果必须使用(如访问资源),应使用 AndroidViewModel(它持有 Application Context),但也要谨慎使用。
  • 推荐在 Repository 层处理需要 Context 的操作(如 SharedPreferences)。

5.3 LiveData 和 Flow 的区别及选择?

  • LiveData:生命周期感知,专为 Android UI 设计,但只能在主线程观察,操作符较少。
  • Flow:Kotlin 协程的一部分,支持复杂操作符(map、flatMap 等),可指定线程调度,但不自带生命周期感知,需配合 repeatOnLifecycle 使用。
  • 选择:简单 UI 状态用 LiveData;复杂数据流、需要协程支持的场景用 Flow + StateFlow。

5.4 MVVM 中如何处理事件(如点击后显示 Toast、导航)?

  • 直接使用 LiveData 可能因旋转导致事件重复触发,因此常用 SingleLiveEvent 或 EventWrapper
class Event<out T>(private val content: T) {
    private var hasBeenHandled = false
    fun getContentIfNotHandled(): T? = if (hasBeenHandled) null else { hasBeenHandled = true; content }
}
  • ViewModel 中暴露 LiveData<Event<String>>,View 中观察并调用 getContentIfNotHandled() 消费事件。

5.5 MVVM 如何避免内存泄漏?

  • 确保 ViewModel 不持有 View 引用(如 Activity、Fragment、View)。
  • 如果 ViewModel 中使用协程,应使用 viewModelScope,它会在 ViewModel 销毁时自动取消协程。
  • 如果 ViewModel 中注册了监听器(如数据库监听),需要在 onCleared() 中注销。

5.6 数据绑定的原理是什么?

  • DataBinding 在编译时会生成一个 Binding 类(如 ActivityMainBinding),该类持有布局中所有带 id 的 View 引用,以及绑定的变量(如 ViewModel)。
  • 当变量值改变时,通过 set 方法触发 notifyPropertyChanged(),Binding 类中的 executeBindings() 方法会更新对应的 View 属性。

6. MVVM 与 MVP 的对比(简要)

  • 通信方式:MVP 通过接口双向通信,MVVM 通过观察者单向数据流。
  • 引用关系:Presenter 持有 View 引用,ViewModel 不持有 View 引用。
  • UI 更新:MVP 手动调用 View 方法,MVVM 自动数据驱动。
  • 测试:MVP 需 Mock View 接口,MVVM 可直接测试 ViewModel。

7. 实际开发中的注意事项

  • 避免 ViewModel 过于臃肿:业务逻辑应下沉到 Repository 或 UseCase。
  • 不要将 Android 框架类(如 Handler、Context)传入 ViewModel
  • 合理使用双向绑定:双向绑定可能引入隐式数据流,增加调试难度,建议仅用于输入控件。
  • 注意线程:LiveData 默认在主线程更新,如果 ViewModel 在后台线程修改 LiveData,需确保 postValue 或切换线程。

8. 总结

MVVM 是 Google 官方推荐的架构模式,通过 ViewModel、LiveData 和 DataBinding 等组件,实现了清晰的分层和高效的 UI 自动更新。它解决了 MVP 中 Presenter 与 View 耦合、手动更新 UI 繁琐等问题,非常适合现代 Android 开发。在实际项目中,应结合 Repository 层管理数据,合理使用协程和 Flow 处理异步,同时注意事件处理、生命周期安全等细节,才能发挥 MVVM 的最大价值。