32、ViewModel与LiveData:ViewModel的创建、LiveData的观察、数据持久化
说到Android架构组件,ViewModel和LiveData绝对是最常用的两个。我刚开始接触它们时,其实有点懵——不就是存个数据、观察个变化吗?用得着搞这么复杂?后来在项目里踩了几次坑,才真正体会到它们的设计精妙之处。
今天我们就来聊聊这两个组件。我会从实际开发的角度出发,把ViewModel的创建、LiveData的观察,以及数据持久化这些事儿讲清楚。
ViewModel:为什么需要它?
先问一个问题:你的Activity或Fragment旋转屏幕后,数据还在吗?
答案是不在。因为旋转屏幕会导致Activity重建,所有成员变量都会被重置。你想想看,用户填了一半的表单,转个屏就没了,这体验能好吗?
ViewModel就是为了解决这个问题而生的。它会在Activity重建时存活下来,数据不会丢失。
核心要点:ViewModel的生命周期独立于Activity/Fragment的配置变化。它会在Activity被销毁(比如finish()调用)时才真正清理。
创建ViewModel:两种方式
我个人习惯用委托方式创建ViewModel,代码更简洁。但传统方式也得会,毕竟有些老项目还在用。
方式一:传统方式
class MyViewModel : ViewModel() {
private val _count = MutableLiveData<Int>(0)
val count: LiveData<Int> get() = _count
fun increment() {
_count.value = (_count.value ?: 0) + 1
}
}
// 在Activity中使用
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this)[MyViewModel::class.java]
}
}
方式二:委托方式(推荐)
class MainActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 直接用viewModel,不用手动创建
}
}
嗯,这里要注意:by viewModels()需要引入activity-ktx依赖。我在项目中遇到过有人忘了加依赖,结果编译报错,排查了半天。
LiveData:观察数据变化
LiveData说白了就是一个可观察的数据容器。它和普通的观察者模式最大的区别是——它感知生命周期。
什么意思呢?就是当Activity在前台时,LiveData才会通知观察者。如果Activity已经销毁,它自动取消订阅。这能有效避免内存泄漏。
小技巧:LiveData的观察回调只在主线程执行。所以更新数据时,如果是在子线程,记得用postValue()而不是setValue()。
观察LiveData
viewModel.count.observe(this) { value ->
textView.text = "当前计数:$value"
}
这段代码看起来简单,但有个坑:observe()的第一个参数是LifecycleOwner。如果你传了this,那LiveData就会跟着Activity的生命周期走。我曾经在Fragment里传了Activity的LifecycleOwner,结果Fragment销毁了观察还在,导致空指针——这坑我踩过。
数据持久化:不仅仅是内存
ViewModel默认只在内存中保存数据。如果进程被系统杀死了,数据还是会丢。那怎么办?
两种思路:
- SavedStateHandle:ViewModel的扩展,能在进程被杀后恢复数据
- 结合Room或DataStore:把数据持久化到本地数据库或文件
使用SavedStateHandle
class MyViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
val count: LiveData<Int> = savedStateHandle.getLiveData("count", 0)
fun increment() {
savedStateHandle["count"] = (savedStateHandle["count"] ?: 0) + 1
}
}
这种方式的好处是:系统自动帮你序列化和反序列化。但注意,它只能存简单类型,复杂对象还是得用Room。
警告:SavedStateHandle默认使用Bundle存储,有大小限制(约1MB)。别往里面塞大对象,否则会崩溃。
知识体系总览
下面这张图是我自己整理的ViewModel与LiveData的核心逻辑,你看一眼就能明白它们之间的关系:
避坑指南
最后分享几个我踩过的坑,希望能帮你少走弯路:
- 不要在ViewModel里持有Activity的Context——这会导致内存泄漏。如果需要Context,用
AndroidViewModel,它持有的是Application Context。 - LiveData的粘性事件——如果你用LiveData做一次性事件(比如弹Toast),要注意它可能会重复触发。我一般用
SingleLiveEvent或EventWrapper来解决。 - ViewModel的Factory——如果ViewModel需要自定义参数,记得写一个Factory。我曾经偷懒用默认构造,结果参数传不进去,调试了半天。
我的习惯:每个页面都创建一个对应的ViewModel,哪怕它现在只存一个变量。因为需求总会变,到时候再拆就麻烦了。
好了,关于ViewModel和LiveData的核心用法就聊到这儿。记住一句话:ViewModel管数据存活,LiveData管数据通知,两者配合,你的页面状态管理会轻松很多。
公众号:蓝海资料掘金营,微信deep3321