Jetpack Compose非Composable方法无法获取ViewModel值解决方案
问题产生原因
一共三个核心问题:
- 你手动通过构造函数创建ViewModel实例的写法是错误的。ViewModel必须绑定对应生命周期的
ViewModelStore才能正常工作,直接new MyViewModel()得到的实例不受生命周期管理,如果你在Composable中反复创建Example类实例,每次都会生成全新的ViewModel对象,之前修改的状态全部存在旧实例里,新实例自然读不到更新后的值。 - Composable函数的执行时机完全由重组调度控制,你用普通成员变量缓存状态值的逻辑不可靠:一方面如果
Example实例在重组中被重建,缓存值会直接重置为初始值;另一方面普通方法调用的时机完全可能早于Composable函数给缓存变量赋值的时机,读到的永远是旧值。 - 你的ViewModel代码本身存在语法和类型错误:内部
_something是MutableLiveData<String>类型,对外暴露的something却声明为LiveData<Int>;空安全合并运算符?:被错误写成了? :(中间多了空格),本身就会导致取值逻辑异常。
修复方案
按以下步骤调整即可:
- 先修正ViewModel本身的代码错误,类名遵循大驼峰规范,修正类型不匹配和运算符写法问题:
class MyViewModel: ViewModel(){ private val _something: MutableLiveData<String> = MutableLiveData("") val something: LiveData<String> = _something fun updateSomething(){ _something.value = "newvalue" } fun getSomeValue(): String { return something.value ?: "" } }
- 禁止手动构造ViewModel实例,在Composable作用域内使用官方提供的
viewModel()扩展函数获取绑定当前生命周期的ViewModel实例,保证Composable内外使用的是同一个ViewModel对象。 - 不要依赖普通成员变量缓存Composable中读取的状态,需要取值时直接从同一个ViewModel实例读取,避免缓存和真实状态不一致的问题。
- 如果你的
Example类需要在Composable外使用,不要在类内部初始化ViewModel,应该把生命周期绑定的同一个ViewModel实例通过参数传入类中使用,避免持有多份ViewModel实例。
参考正确的实现代码:
class Example { private var holdSomething: String = "" @Composable private fun MyComposable() { // 正确获取当前作用域的ViewModel实例 val state: MyViewModel = viewModel() state.updateSomething() holdSomething = state.getSomeValue() } /** * 传入和Composable中使用的同一个ViewModel实例,保证取值正确 * 不要依赖类内部自己new的ViewModel实例 */ fun getSomeValue(state: MyViewModel): String { return state.getSomeValue() } }
额外注意:不要在Composable函数内部反复创建Example类实例,否则每次重组都会生成新的对象,所有成员状态都会重置。如果需要在Composable中持有Example实例,用remember包裹保证重组期间实例复用:
@Composable fun TestScreen() { // 错误写法:每次重组都创建新Example // val example = Example() // 正确写法:重组期间复用同一个Example实例 val example = remember { Example() } example.MyComposable() }
内容的提问来源于stack exchange,提问作者EMT
相关产品推荐
相关产品推荐

