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 组件栈
- ViewModel:
androidx.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 的最大价值。