错误:
Recomposer.applyAndCheck
java.lang.IllegalStateException - Unsupported concurrent change during composition. A state object was modified by composition as well as being modified outside composition.
我有时会遇到不支持的并发更改的错误。 我尝试了解问题的根源。当我学习可组合性时,我使用
coroutineScope with Dispatchers.IO
来更新状态,它导致了同样的问题。我已阅读 https://developer.android.com/develop/ui/compose/side-effects 并且我在代码中没有看到任何问题。
这是什么问题? 也许在状态变化期间我应该使用 viewModelScope~?
is HomeEvent.OnFreeCouponDeclined -> {
homeState.update {
copy(isDeclined = false)
}
}
应该是吗?
is HomeEvent.OnFreeCouponDeclined -> {
viewModelScope.launch {
homeState.update {
copy(isDeclined = false)
}
}
}
也许这里,会不会是错的?
private fun observeInventory() {
viewModelScope.launch {
(Dispatchers.Default) {
getInventoryInteractor.invoke().collectLatest {
(Dispatchers.Main) {
homeState.value = homeState.value.copy(categories = it.map {
it.copy(subCategories = emptyList())
})
}
}
}
}
}
看起来我有时使用
viewModelScope
来改变状态,有时则不使用。
请我向您寻求建议,我可以寻找什么?
也许这个代码?
LaunchedEffect("${state.currentPage}_${sliderHoldTimestamp}_${sliderItems.size}") {
delay(AUTO_SLIDER_DELAY)
coroutineScope.launch {
if (sliderItems.isNotEmpty() && state.pageCount != 0) {
var newPosition = state.currentPage + 1
if (newPosition > sliderItems.size - 1) newPosition = 0
state.animateScrollToPage(newPosition.mod(state.pageCount))
}
}
}
这是自动滑块,由于从
accompanist
更新到更新的方法,我必须像这样解决它。
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
const val foundation = "androidx.compose.foundation:foundation:1.6.1"
这可能是由于同时从不同线程更新 State 对象导致的。
解决此问题的最有效方法是将更新 State 对象限制在主线程,最好只限于可组合项。总体思路是视图模型检索数据并将其公开为 StateFlow(尽管它的名称如此,但与 Compose State 无关)。这样,您就不会更新视图模型中的任何 State 对象(因为没有),并且只有您的可组合项最终会收集流。这只会发生在主线程上,不应该再有任何并发更新。
您只提供了视图模型的外观及其功能的部分图片,但我最好的猜测是这可能是一个足够的替代品:
private val isDeclined = MutableStateFlow(false)
val homeState: StateFlow<HomeState?> = combine(
getInventoryInteractor(),
isDeclined,
) { inventory, isDeclined ->
HomeState(
categories = inventory.map {
it.copy(subCategories = emptyList())
},
isDeclined = isDeclined,
)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = null,
)
fun handleEvent(event: HomeEvent) {
when (event) {
is HomeEvent.OnFreeCouponDeclined -> isDeclined.value = false
}
}
如您所见,没有收集任何流,也没有更新状态对象,只有现有流被转换。我还为
isDeclined
引入了一个新的轻量级 MutableStateFlow,因此它可以与从 getInventoryInteractor
返回的流相结合。您可能希望将 initialValue
的 stateIn
更改为 homeState
应有的值,直到所有流都提供其第一个值。
您的实际代码可能更复杂,但您应该能够重构它以符合新的结构。
在可组合项中,您只需收集
homeState
流:
val homeState by viewModel.homeState.collectAsStateWithLifecycle()
为此,您需要 gradle 依赖项
androidx.lifecycle:lifecycle-runtime-compose
。