Compose中ViewModel的init块重复执行问题求助
问题原因及解决方案
核心问题:ViewModel被重复实例化
你的ExamplesScreen组件中,使用了默认参数直接创建ViewModel:
viewModel: ExampleViewModel = ExampleViewModel(section!!)
Composable每次重组时,都会执行这个默认参数的初始化逻辑,导致每次重组都会生成一个全新的ExampleViewModel实例。新实例的init块会再次触发网络请求并更新UI状态,这直接造成了加载状态与列表状态反复切换的闪烁问题。
另外,原ViewModel中两次分开的状态更新会触发两次UI重组,也会加重闪烁现象:
// 两次独立update会触发两次状态发射 _uiState.update { it.copy(isLoading = false) } _uiState.update { it.copy(samples = samplesList) }
修复步骤
正确获取ViewModel实例
不要在Composable默认参数中直接实例化ViewModel,改用viewModel()函数(依赖androidx.lifecycle:lifecycle-viewmodel-compose),它会通过ViewModelProvider保证同一生命周期内仅创建一个ViewModel实例。由于你的ViewModel有构造参数,需要自定义Factory:// 自定义ViewModel工厂类 class ExampleViewModelFactory(private val section: String) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(ExampleViewModel::class.java)) { return ExampleViewModel(section) as T } throw IllegalArgumentException("未知的ViewModel类") } } // 修改Composable的ViewModel获取方式 @Composable fun ExamplesScreen( navController: NavController, section: String?, viewModel: ExampleViewModel = viewModel(factory = ExampleViewModelFactory(section!!)) ) { // ... 原有UI代码 }合并UI状态更新
将两次状态更新合并为一次,减少不必要的UI重组:init { viewModelScope.launch(Dispatchers.IO) { _uiState.update { it.copy(isLoading = true) } val samplesList = exampleRepository.getSamplesBySection(section) // 一次update完成所有状态变更 _uiState.update { it.copy( isLoading = false, samples = samplesList.takeIf { it.isNotEmpty() } ?: emptyList(), isError = samplesList.isEmpty() // 可补充错误状态处理 ) } } }
内容的提问来源于stack exchange,提问作者jojeyh
相关产品推荐
相关产品推荐

