47、ViewModel与LiveData:ViewModel生命周期感知、LiveData观察数据、数据共享
说实话,ViewModel 和 LiveData 这对组合,是我在 Android 开发中用得最顺手的搭档之一。刚入行那会儿,我还在用 AsyncTask 和 Handler 处理异步数据,Activity 一旋转屏幕数据就丢了,那叫一个头疼。后来 Google 推出了 Jetpack,我才算真正解脱了。
今天咱们就聊聊这两个东西。说白了,ViewModel 帮你管数据,LiveData 帮你通知界面。它们配合起来,能让你的代码干净不少。
ViewModel:生命周期感知的数据容器
ViewModel 的核心价值是什么?我总结了一句话:它让你的数据在屏幕旋转时活下来。
你想想看,Activity 重建时,里面的成员变量全没了。但 ViewModel 不会。它被存储在 ViewModelStore 里,Activity 重建后拿到的还是同一个 ViewModel 实例。
关键点:ViewModel 的生命周期比 Activity/Fragment 长。它会在 Activity 真正 finish 后才被清除。
来看一个最简单的例子:
public class MyViewModel extends ViewModel {
private MutableLiveData<String> userName = new MutableLiveData<>();
public LiveData<String> getUserName() {
return userName;
}
public void setUserName(String name) {
userName.setValue(name);
}
}
在 Activity 里这样用:
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.getUserName().observe(this, name -> {
textView.setText(name);
});
嗯,这里要注意:千万不要把 Context 传给 ViewModel。我见过有人把 Activity 实例传进去,结果内存泄漏了。ViewModel 的生命周期比 Activity 长,持有 Activity 引用会导致它无法被回收。
LiveData:观察者模式的优雅实现
LiveData 是个可观察的数据持有者。它跟普通的 Observable 最大的区别是什么?它知道观察者的生命周期。
举个例子。你用 LiveData 在 Activity 里观察数据,当 Activity 处于 STARTED 状态时,它才会通知。如果 Activity 已经 DESTROYED,它会自动移除观察者。这省去了你手动管理注册/注销的麻烦。
我个人习惯把 LiveData 分成两种:
- MutableLiveData:可写,只在 ViewModel 内部暴露
- LiveData:只读,对外暴露给 View 层
这样做的好处是,View 层只能观察数据,不能修改数据。数据修改的入口统一在 ViewModel 里,逻辑清晰。
小技巧:如果你需要转换 LiveData 的数据,可以用 Transformations.map() 或 switchMap()。比如把 User 对象映射成显示用的字符串。
LiveData<String> displayName = Transformations.map(userLiveData, user -> {
return user.getFirstName() + " " + user.getLastName();
});
数据共享:多个 Fragment 共用一个 ViewModel
这是 ViewModel 的一个杀手级应用。两个 Fragment 在同一个 Activity 里,它们可以共享同一个 ViewModel 实例。
怎么做?很简单,用 Activity 作为 ViewModelProvider 的 scope:
// Fragment A
SharedViewModel vm = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
// Fragment B
SharedViewModel vm = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
两个 Fragment 拿到的 vm 是同一个对象。A 修改了数据,B 通过 LiveData 观察就能收到通知。
我曾经在一个电商项目里用过这个模式。商品列表 Fragment 和购物车 Fragment 共享一个 ViewModel。用户点击加入购物车,列表 Fragment 更新 ViewModel 里的数据,购物车 Fragment 自动刷新数量。代码量少了一半,而且没有乱七八糟的接口回调。
避坑指南
我曾经踩过的坑:
- LiveData 在 Fragment 中重复观察:Fragment 重建时,如果 onViewCreated 里又调了一次 observe,会导致回调执行多次。解决办法是用 getViewLifecycleOwner() 代替 this。
- ViewModel 里做耗时操作:ViewModel 不能持有 Activity 引用,但可以用 Application Context。如果需要网络请求,建议结合 Repository 模式,把数据获取逻辑抽出去。
- LiveData 的粘性事件:LiveData 在观察时会立即收到最新值。如果你只想接收后续变化,可以用 SingleLiveEvent 或 Event 包装类。
核心流程示意图
下面这张图展示了 ViewModel 和 LiveData 在 Activity 生命周期中的协作关系:
总结一下
ViewModel 和 LiveData 解决的是 Android 开发中最常见的问题:数据与生命周期解耦。ViewModel 负责存数据,LiveData 负责通知变化。它们配合使用,能让你的代码更健壮、更易维护。
我个人建议,新项目直接上 ViewModel + LiveData,别再用那些老掉牙的方案了。你想想看,少写多少模板代码,少踩多少坑,值不值?
一句话记住:ViewModel 是数据的家,LiveData 是家的门铃。家不倒,门铃响。
公众号:蓝海资料掘金营,微信deep3321