Jetpack Compose中视图消失后ViewModel未被清除的原因排查
为什么你的ViewModel没有被清除?
你的MyViewModel未触发onCleared()的核心原因是ViewModel的作用域绑定逻辑不符合预期,具体细节如下:
1. 默认viewModel()的作用域是Activity/Fragment
在Compose中调用val viewModel: MyViewModel = viewModel()时,默认会将ViewModel的生命周期绑定到宿主ComponentActivity(也就是你的MainActivity),而非StateZero这个Composable的生命周期。
这意味着:
- 只有当
MainActivity被销毁时,ViewModel才会触发onCleared() - 即便
StateZero因x的状态变化被从Composition中移除,ViewModel仍会保留在Activity的作用域内,不会被清除
2. 让ViewModel随Composable销毁而清除的方法
如果你希望ViewModel和StateZero的生命周期绑定,需要使用Compose专属的ViewModel作用域API,通过指定自定义的ViewModelStoreOwner实现:
修改StateZero的代码:
@Composable fun StateZero() { // 创建与当前Composable绑定的ViewModel存储容器 val viewModelStoreOwner = remember { object : ViewModelStoreOwner { private val store = ViewModelStore() override fun getViewModelStore(): ViewModelStore = store // 当Composable被移除时,清空存储容器,触发ViewModel的onCleared DisposableEffect(Unit) { onDispose { store.clear() } } } } val viewModel: MyViewModel = viewModel(viewModelStoreOwner = viewModelStoreOwner) Text(text = "Should be clearing in a few seconds...") }
或者使用更简洁的rememberViewModel(需依赖androidx.lifecycle:lifecycle-viewmodel-compose最新版本):
@Composable fun StateZero() { val viewModel: MyViewModel = rememberViewModel() Text(text = "Should be clearing in a few seconds...") }
rememberViewModel()会自动将ViewModel的生命周期绑定到当前Composable,当Composable从Composition中移除时,ViewModel会被清除并触发onCleared()。
3. 原代码的逻辑验证
你的代码中x的状态每隔1秒切换一次,StateZero和Text("NO")交替显示,但由于ViewModel绑定到Activity,每次StateZero重新进入Composition时,拿到的都是同一个ViewModel实例,且只有Activity销毁时才会触发onCleared()。
内容的提问来源于stack exchange,提问作者J. Doe
相关产品推荐
相关产品推荐

