Android ViewModel 深度解析:原理、使用与最佳实践
一、ViewModel 概述
ViewModel 是 Android Jetpack 架构组件中的重要一员,专门为解决 Activity 和 Fragment 中的 UI 数据管理问题而设计。它的核心目标是:
管理 UI 相关数据:以生命周期感知的方式保存和管理数据
解决配置变更问题:在屏幕旋转等配置更改时保留数据
避免内存泄漏:自动清理资源,防止 Activity/Fragment 引用泄漏
// 基本ViewModel类定义 class MyViewModel : ViewModel() {// 数据将在此保存var counter = 0 }
二、ViewModel 生命周期
理解 ViewModel 的生命周期是其正确使用的关键:
ViewModel 的生命周期比创建它的 Activity/Fragment 更长
在 Activity 完成(finish)时才会清除
屏幕旋转等配置变化不会导致重建
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 获取ViewModel实例val model: MyViewModel by viewModels()} }
三、ViewModel 的基本使用
1. 添加依赖
首先在 build.gradle 中添加依赖:
dependencies {implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2"// 如果使用ViewModel带SavedStateimplementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2" }
2. 创建 ViewModel 类
class UserViewModel : ViewModel() {private val _users = MutableLiveData<List<User>>()val users: LiveData<List<User>> = _usersinit {loadUsers()}private fun loadUsers() {// 模拟数据加载_users.value = listOf(User("张三"), User("李四"))} }
3. 在 Activity/Fragment 中使用
class UserActivity : AppCompatActivity() {private val userViewModel: UserViewModel by viewModels()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_user)userViewModel.users.observe(this) { users ->// 更新UIupdateUserList(users)}}
}
四、ViewModel 的高级特性
1. ViewModel 带参数
如果需要传递参数给 ViewModel,可以使用 ViewModelProvider.Factory:
class UserViewModel(private val userId: String) : ViewModel() {// ...
}class UserViewModelFactory(private val userId: String) : ViewModelProvider.Factory {override fun <T : ViewModel> create(modelClass: Class<T>): T {return UserViewModel(userId) as T}
}// 使用
val factory = UserViewModelFactory("123")
val viewModel = ViewModelProvider(this, factory).get(UserViewModel::class.java)
2. SavedStateHandle
处理进程死亡后恢复数据:
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() {val counter: LiveData<Int> = state.getLiveData("counter", 0)fun increment() {state["counter"] = (counter.value ?: 0) + 1}
}
3. 在 Fragment 间共享数据
class SharedViewModel : ViewModel() {val selectedItem = MutableLiveData<Item>()fun select(item: Item) {selectedItem.value = item}
}// FragmentA
val model: SharedViewModel by activityViewModels()// FragmentB
val model: SharedViewModel by activityViewModels()
五、ViewModel 最佳实践
随着 Jetpack 组件的不断演进,ViewModel 也在持续增强功能(如与 Hilt 的集成、更完善的状态保存机制等),值得开发者持续关注和学习。
职责单一:每个 ViewModel 应只负责一个屏幕或功能模块
避免引用 Context:ViewModel 不应持有 Activity/Fragment 的引用
合理使用 LiveData:暴露不可变的 LiveData,内部使用 MutableLiveData
结合 Repository:数据操作应委托给 Repository 层
测试友好:ViewModel 应易于单元测试
// 良好结构的ViewModel示例 class OrderViewModel(private val orderRepository: OrderRepository,private val savedStateHandle: SavedStateHandle ) : ViewModel() {private val _order = MutableLiveData<Order>()val order: LiveData<Order> = _orderprivate val _loading = MutableLiveData<Boolean>()val loading: LiveData<Boolean> = _loadingfun loadOrder(orderId: String) {_loading.value = trueviewModelScope.launch {try {_order.value = orderRepository.getOrder(orderId)} catch (e: Exception) {// 处理错误} finally {_loading.value = false}}} }
六、常见问题与解决方案
1. ViewModel 内存泄漏
问题:在 ViewModel 中持有 Activity/Fragment 引用
解决:使用 Application Context 或完全避免 Context2. 数据重复加载
问题:每次配置变更都重新加载数据
解决:在 ViewModel 中缓存数据class MyViewModel : ViewModel() {private var cachedData: List<Data>? = nullfun getData(): LiveData<List<Data>> {if (cachedData == null) {loadData()}return Transformations.map(_source) { it }} }
3. 测试困难
解决:依赖注入和接口抽象
class MyViewModel(private val dataSource: DataSourceInterface ) : ViewModel() {// ... }// 测试时可以传入Mock实现
七、ViewModel 与协程
ViewModel 内置了 viewModelScope,便于协程管理:
class CoroutineViewModel : ViewModel() {fun fetchData() {viewModelScope.launch {try {val data = repository.fetchData()_uiState.value = UiState.Success(data)} catch (e: Exception) {_uiState.value = UiState.Error(e)}}} }
结语
ViewModel 是现代 Android 开发中不可或缺的架构组件,它优雅地解决了 UI 控制器(Activity/Fragment)中数据管理的难题。通过合理使用 ViewModel,开发者可以:
构建更健壮、更易维护的应用程序
提高代码的可测试性
减少内存泄漏的风险
提供更好的用户体验