Android MVVM 架构:ViewModel + LiveData + DataBinding
什么是 MVVM 架构
MVVM(Model–View–ViewModel)是一种将 UI 逻辑与业务逻辑分离的架构模式,特别适合 Android 这类经常需要处理生命周期和配置变更的平台。它由三个核心组件构成:
- Model:负责数据获取、存储与业务处理(如网络请求、数据库操作)。
- View:负责展示 UI 并接收用户交互(Activity、Fragment、XML 布局)。
- ViewModel:连接 View 和 Model,持有 UI 所需数据,处理 UI 交互逻辑,并具备生命周期感知能力。
借助 Android Jetpack 提供的 ViewModel、LiveData 和 DataBinding,可以快速实现 MVVM,降低样板代码,提升可测试性和可维护性。
搭建 MVVM 的三大支柱
ViewModel:生命周期感知的数据持有者
ViewModel 类在 Activity 或 Fragment 重建时(如屏幕旋转)能够保留数据,避免重新加载。它会与 UI 控制器的生命周期关联,在宿主彻底销毁时自动清理,防止内存泄漏。
使用方式:
class MyViewModel : ViewModel() {
// 待后面添加 LiveData
}
在 View 中获取:
private val viewModel: MyViewModel by viewModels()
LiveData:可观察的数据容器
LiveData 是一个可观察的数据持有者,它尊重组件生命周期,仅向活跃的观察者(处于 STARTED 或 RESUMED 状态的组件)分送更新。这有效避免了因已销毁的 View 更新导致崩溃。
典型用法:在 ViewModel 中暴露 LiveData,由 View 订阅。
class MyViewModel : ViewModel() {
private val _userName = MutableLiveData<String>()
val userName: LiveData<String> = _userName
fun loadUser() {
_userName.value = "Alice"
}
}
View 中观察:
viewModel.userName.observe(viewLifecycleOwner) { name ->
// 更新 UI,如 binding.textView.text = name
}
DataBinding:布局与数据的声明式绑定
DataBinding 库允许直接在 XML 布局中绑定数据源,减少 findViewById 和手动设置视图代码。配合 MVVM 可实现“数据驱动 UI”,状态变化会自动反映在界面上。
启用 DataBinding:在模块级 build.gradle 中配置
android {
...
buildFeatures {
dataBinding true
}
}
布局文件根标签改为 <layout>,内部包含 <data> 区域声明变量:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.example.MyViewModel" />
</data>
<LinearLayout ...>
<TextView
android:text="@{viewModel.userName}" />
</LinearLayout>
</layout>
在 Activity/Fragment 中绑定:
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.lifecycleOwner = this // 以便 LiveData 能感知生命周期
binding.viewModel = viewModel
}
实战:构建一个用户信息展示页
项目结构
com.example.mvvmdemo
├── model
│ └── UserRepository.kt // 模拟数据源
├── viewmodel
│ └── UserViewModel.kt
├── view
│ └── MainActivity.kt
└── res/layout
└── activity_main.xml
Model 层:模拟数据仓库
// UserRepository.kt
class UserRepository {
fun getUserName(): String {
// 实际项目中可能是网络或数据库请求
return "Alice"
}
}
ViewModel 层:连接 UI 与数据
// UserViewModel.kt
class UserViewModel : ViewModel() {
private val repository = UserRepository()
private val _userName = MutableLiveData<String>()
val userName: LiveData<String> = _userName
fun loadUser() {
_userName.value = repository.getUserName()
}
}
View 层:Activity 与布局
布局文件 activity_main.xml:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.example.mvvmdemo.viewmodel.UserViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tvUserName"
android:text="@{viewModel.userName}"
android:textSize="24sp" />
<Button
android:text="加载用户"
android:onClick="@{() -> viewModel.loadUser()}" />
</LinearLayout>
</layout>
Activity 代码:
// MainActivity.kt
class MainActivity : AppCompatActivity() {
private val viewModel: UserViewModel by viewModels()
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.lifecycleOwner = this
binding.viewModel = viewModel
}
}
当按钮被点击,loadUser() 被调用,LiveData 更新,TextView 自动显示 “Alice”。
进阶:处理用户交互与双向绑定
除了显示数据,MVVM 也支持通过 ViewModel 处理用户输入。DataBinding 提供双向绑定 @={} 语法,常见于 EditText。
在 ViewModel 中管理输入状态
class LoginViewModel : ViewModel() {
val username = MutableLiveData<String>() // 用于双向绑定
val password = MutableLiveData<String>()
fun login() {
// 处理登录逻辑
}
}
布局中的双向绑定
<EditText
android:text="@={viewModel.username}" />
<EditText
android:text="@={viewModel.password}"
android:inputType="textPassword" />
<Button
android:onClick="@{() -> viewModel.login()}"
android:text="登录" />
这样无需在代码中获取文本,ViewModel 的字段会实时反映输入内容。
MVVM 的优势与适用场景
- 分离关注点:视图只负责展示,逻辑集中在 ViewModel,易于单元测试。
- 生命周期安全:LiveData + ViewModel 避免因配置变更导致的数据丢失或内存泄漏。
- 降低耦合:View 和 ViewModel 通过数据绑定通信,不持有引用,便于替换和重构。
- 声明式 UI:DataBinding 减少模板代码,布局文件直接表达 UI 状态。
适合大多数应用,尤其是数据驱动、交互复杂的界面。不适合过于简单的静态页面,避免过度设计。
避免常见陷阱
不要在 ViewModel 中持有 View 引用
ViewModel 的生命周期比 View 长,持有 Activity/Fragment 或 View 会导致内存泄漏。使用 LiveData 暴露数据,由 View 自己观察。
正确使用 LiveData 观察
必须在 observe() 方法中传入 LifecycleOwner(如 viewLifecycleOwner),否则可能导致 Fragment 在 onDestroyView 后仍更新已销毁的 UI。
viewModel.userName.observe(viewLifecycleOwner) { /* 更新 UI */ }
避免在 ViewModel 中进行耗时操作
ViewModel 主线程执行,长时间操作应使用 viewModelScope 启动协程,或配合 LiveData 构建器。
class NewsViewModel : ViewModel() {
val news: LiveData<List<News>> = liveData {
// 自动使用 viewModelScope,并在数据加载完成时切换回主线程
emit(repository.fetchNews())
}
}
不要过度使用双向绑定
双向绑定方便但会让代码逻辑隐式化,复杂验证逻辑建议留在 ViewModel 中明确处理,而非通过布局表达式。
总结
MVVM 是 Android 开发中成熟且高效的架构选择。通过组合 ViewModel、LiveData 和 DataBinding,你能够构建出结构清晰、响应灵敏、生命周期安全的应用。从简单的数据显示到复杂的表单交互,掌握这三项技术将为你的代码带来质的提升。建议在实际项目中逐步引入,感受数据驱动 UI 的顺畅体验。