Android MVVM 架构:ViewModel + LiveData + DataBinding

FreeGuideOnline 最新 2026-06-17

什么是 MVVM 架构

MVVM(Model–View–ViewModel)是一种将 UI 逻辑与业务逻辑分离的架构模式,特别适合 Android 这类经常需要处理生命周期和配置变更的平台。它由三个核心组件构成:

  • Model:负责数据获取、存储与业务处理(如网络请求、数据库操作)。
  • View:负责展示 UI 并接收用户交互(Activity、Fragment、XML 布局)。
  • ViewModel:连接 View 和 Model,持有 UI 所需数据,处理 UI 交互逻辑,并具备生命周期感知能力。

借助 Android Jetpack 提供的 ViewModelLiveDataDataBinding,可以快速实现 MVVM,降低样板代码,提升可测试性和可维护性。

搭建 MVVM 的三大支柱

ViewModel:生命周期感知的数据持有者

ViewModel 类在 ActivityFragment 重建时(如屏幕旋转)能够保留数据,避免重新加载。它会与 UI 控制器的生命周期关联,在宿主彻底销毁时自动清理,防止内存泄漏。

使用方式:

class MyViewModel : ViewModel() {
    // 待后面添加 LiveData
}

在 View 中获取:

private val viewModel: MyViewModel by viewModels()

LiveData:可观察的数据容器

LiveData 是一个可观察的数据持有者,它尊重组件生命周期,仅向活跃的观察者(处于 STARTEDRESUMED 状态的组件)分送更新。这有效避免了因已销毁的 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 开发中成熟且高效的架构选择。通过组合 ViewModelLiveDataDataBinding,你能够构建出结构清晰、响应灵敏、生命周期安全的应用。从简单的数据显示到复杂的表单交互,掌握这三项技术将为你的代码带来质的提升。建议在实际项目中逐步引入,感受数据驱动 UI 的顺畅体验。