Android:按官方文档在ViewModel中使用by lazy是否合理?
在ViewModel中使用by lazy初始化LiveData是否合理?
Android官方文档给出的ViewModel示例代码如下:
class MyViewModel : ViewModel() { private val users: MutableLiveData<List<User>> by lazy { MutableLiveData<List<User>>().also { loadUsers() } } fun getUsers(): LiveData<List<User>> { return users } private fun loadUsers() { // 执行异步操作获取用户数据 } }
我对在ViewModel中用by lazy初始化LiveData的写法存疑,因为接触的项目里都没这么写过。比如如果把loadUsers()改成下面这样:
private fun loadUsers() { // 执行异步操作获取用户数据 users.value = ... }
这时候调用loadUsers()会再次访问users变量,而它还没完成初始化,就会导致loadUsers()被重复调用——比如在Activity或Fragment中观察这个LiveData时,就会触发这个问题。
问题根源分析
这段官方示例的写法存在明显的初始化循环依赖问题:
- 当第一次访问
users时,lazy块会创建MutableLiveData实例,紧接着调用loadUsers() - 而修改后的
loadUsers()又去访问users.value,此时lazy块还没执行完(users变量还没完成赋值),就会再次触发lazy初始化逻辑,导致loadUsers()被递归调用,最终可能引发栈溢出或者重复发起异步请求的问题。
为什么官方会这么写?
官方示例里的loadUsers()只是占位逻辑,没有访问users变量,所以不会触发循环。但这种写法很容易误导开发者,实际项目中几乎不会这么用——因为只要loadUsers()需要更新users的值,就必然会踩坑。
更合理的实现方式
实际项目中,更稳妥的写法是提前初始化LiveData,再单独触发数据加载逻辑,避免循环依赖:
class MyViewModel : ViewModel() { private val users = MutableLiveData<List<User>>() init { loadUsers() } fun getUsers(): LiveData<List<User>> { return users } private fun loadUsers() { // 执行异步操作获取用户数据 users.value = ... } }
或者如果需要懒加载(比如只有当UI观察数据时才加载),可以通过标记位实现安全的懒加载:
class MyViewModel : ViewModel() { private val users = MutableLiveData<List<User>>() private var isLoaded = false fun getUsers(): LiveData<List<User>> { if (!isLoaded) { loadUsers() isLoaded = true } return users } private fun loadUsers() { // 执行异步操作获取用户数据 users.value = ... } }
总结
- 官方示例的写法仅适用于
loadUsers()不依赖users变量的场景,实际项目中几乎没有实用价值 by lazy+ 在初始化块中调用会修改自身的方法,本质是错误的写法,会引发循环初始化问题- 优先选择提前初始化LiveData并在
init块加载数据,或者通过标记位实现安全的懒加载
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

