使用MutableStateFlow与Compose时遇ConcurrentModificationException如何解决?
解决MutableStateFlow并发修改异常的方案
问题根源
你的ConcurrentModificationException本质是可变集合的并发读写冲突:
- 如果
SearchState中的currentItems是MutableList这类可变集合,当后台线程的Callback更新状态时,Compose正同时在主线程遍历该集合,就会触发异常。 - 数据类的默认
copy()方法对集合是浅拷贝,新旧状态会共享同一个可变集合实例,后续修改会直接影响正在使用的旧状态。
具体解决步骤
1. 使用不可变集合,切断共享引用
把currentItems的类型改为不可变集合(比如List),每次更新时创建新的不可变实例:
// 定义SearchState时指定不可变集合类型 data class SearchState(val currentItems: List<SearchRowItemData> = emptyList()) // ViewModel中更新状态时,将传入的items转为不可变List searchState.value = searchState.value.copy( currentItems = items.toList() // 强制转为不可变集合,避免共享可变引用 )
2. 强制在主线程更新StateFlow
MutableStateFlow的value更新本身线程安全,但跨线程更新可变状态容易引发UI侧的并发问题。确保状态更新操作在主线程执行:
private fun getItems() = viewModelScope.launch { .... object : Callback<Results> { override fun publish(items: Results): Boolean { // 切换到主线程更新状态 viewModelScope.launch(Dispatchers.Main) { searchState.value = searchState.value.copy( currentItems = items.toList() ) } return true } } }
3. 遍历前获取集合快照(可选兜底方案)
如果无法避免使用可变集合,在Compose遍历前先获取集合的快照,避免遍历过程中集合被修改:
@Composable fun ScreenContent(viewModel: SearchViewModel) { val state by viewModel.searchState.collectAsState() // 先获取集合快照再遍历 val itemsSnapshot = state.currentItems.toList() Column(modifier = Modifier.fillMaxWidth()) { itemsSnapshot.forEachIndexed { index, searchRowItemData -> SearchRowItem( searchRowItemData, index == itemsSnapshot.lastIndex ) { onItemClicked(searchRowItemData) } } } }
4. 确认Callback的执行线程
检查你的Callback的publish方法是否在后台线程运行,如果是,必须切换到主线程更新状态——Compose的重组和UI操作都在主线程,跨线程操作可变状态是并发问题的常见诱因。
内容的提问来源于stack exchange,提问作者P.Juni
相关产品推荐
相关产品推荐

