ViewModel基础:创建、生命周期与通信
大家好,我是你们的老朋友。今天我们来聊聊ViewModel——这个在Android开发中几乎天天打交道的组件。
说实话,我刚接触ViewModel时,觉得它不就是个存数据的盒子吗?后来踩了不少坑才明白,它的生命周期管理才是真正的精髓。你想想看,如果Activity旋转一下,数据就丢了,用户得多崩溃?
ViewModel的创建
创建ViewModel其实很简单,但背后有不少门道。我个人习惯先定义一个ViewModel类,继承自Android系统提供的ViewModel基类:
class MyViewModel : ViewModel() {
private val _userName = MutableLiveData<String>()
val userName: LiveData<String> get() = _userName
fun loadUser() {
_userName.value = "张三"
}
}
嗯,这里要注意一点:不要在ViewModel里持有Activity或Fragment的引用。我在项目中遇到过有人把Context传进去,结果内存泄漏了,排查了半天才找到原因。
ViewModel的生命周期
ViewModel的生命周期和Activity/Fragment不一样。它会在Activity创建时生成,直到Activity真正销毁(不是旋转屏幕那种销毁)才会被清除。
核心要点:ViewModel的生命周期跨越了Activity的配置变更。屏幕旋转时,Activity重建了,但ViewModel还是同一个对象。
我画了一张图,帮你理解这个生命周期关系:
小提示:如果你需要在ViewModel销毁时做一些清理工作(比如取消网络请求),可以重写onCleared()方法。我曾经在这里忘记取消一个RxJava订阅,结果导致内存泄漏,教训深刻啊。
ViewModelProvider的使用
有了ViewModel类,怎么在Activity或Fragment里拿到它呢?答案是ViewModelProvider。
// 在Activity中获取ViewModel
val viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
// 在Fragment中获取ViewModel
val viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
这里有个细节:ViewModelProvider(this)中的this指的是ViewModelStoreOwner。Activity和Fragment都实现了这个接口,所以可以直接传进去。
如果你需要在Fragment之间共享数据,可以用Activity作为作用域:
// FragmentA 和 FragmentB 都这样获取,就能共享同一个ViewModel实例
val sharedViewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
注意:千万不要在ViewModel的构造方法里做耗时操作。ViewModel的创建是在主线程完成的。如果需要异步加载数据,建议使用LiveData配合协程或RxJava。
ViewModel与Activity/Fragment的通信
通信方式主要有两种:
- ViewModel → View:通过LiveData观察数据变化
- View → ViewModel:直接调用ViewModel的方法
看个完整的例子:
// ViewModel
class UserViewModel : ViewModel() {
private val _user = MutableLiveData<User>()
val user: LiveData<User> get() = _user
fun fetchUser(userId: String) {
// 模拟网络请求
_user.value = User(userId, "加载中...")
}
}
// Activity
class UserActivity : AppCompatActivity() {
private lateinit var viewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
// 观察数据变化
viewModel.user.observe(this) { user ->
// 更新UI
textView.text = user.name
}
// 用户点击按钮触发加载
button.setOnClickListener {
viewModel.fetchUser("123")
}
}
}
说白了,ViewModel就是View的数据管家。View只管展示,ViewModel负责管理数据。这种分离让代码更清晰,也更容易测试。
最佳实践:我个人建议在ViewModel中只暴露LiveData(不可变),内部使用MutableLiveData。这样外部只能观察,不能修改,保证了数据流向的单向性。
避坑指南
我曾经踩过几个坑,分享给大家:
- 不要在ViewModel里保存大量数据——比如Bitmap列表。ViewModel虽然存活时间长,但内存还是有限的。我有个项目因为保存了图片缓存,导致OOM,后来改用Room数据库才解决。
- 不要直接暴露MutableLiveData——外部可以直接修改数据,破坏了封装性。用LiveData做返回类型,内部用MutableLiveData。
- Fragment之间共享ViewModel时注意作用域——用requireActivity()作为作用域,两个Fragment才能拿到同一个实例。如果用this,那就各自独立了。
嗯,ViewModel的基础知识就这些。记住它的核心价值:在配置变更时保持数据存活。你想想看,如果没有ViewModel,每次旋转屏幕都要重新加载数据,用户得多烦躁?
好了,今天的分享就到这里。如果你在实际开发中遇到什么问题,欢迎交流讨论。