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 与 Activity 生命周期对比 Activity 生命周期 onCreate onStart onResume 运行中 onDestroy ViewModel 生命周期 ViewModel 创建 ViewModel 存活(跨越配置变更) onCleared 创建 真正销毁 屏幕旋转时:Activity 走 onDestroy → onCreate,但 ViewModel 保持不变 只有 Activity 真正 finish() 后,ViewModel 才会调用 onCleared()

小提示:如果你需要在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。这样外部只能观察,不能修改,保证了数据流向的单向性。

避坑指南

我曾经踩过几个坑,分享给大家:

  1. 不要在ViewModel里保存大量数据——比如Bitmap列表。ViewModel虽然存活时间长,但内存还是有限的。我有个项目因为保存了图片缓存,导致OOM,后来改用Room数据库才解决。
  2. 不要直接暴露MutableLiveData——外部可以直接修改数据,破坏了封装性。用LiveData做返回类型,内部用MutableLiveData。
  3. Fragment之间共享ViewModel时注意作用域——用requireActivity()作为作用域,两个Fragment才能拿到同一个实例。如果用this,那就各自独立了。

嗯,ViewModel的基础知识就这些。记住它的核心价值:在配置变更时保持数据存活。你想想看,如果没有ViewModel,每次旋转屏幕都要重新加载数据,用户得多烦躁?

好了,今天的分享就到这里。如果你在实际开发中遇到什么问题,欢迎交流讨论。


公众号:蓝海资料掘金营,微信deep3321