如何解决“组合期间不支持并发更改”?

问题描述 投票:0回答:1

错误:

Recomposer.applyAndCheck
java.lang.IllegalStateException - 组合期间不支持并发更改。状态对象可以通过组合进行修改,也可以在组合之外进行修改。

我有时会遇到不支持的并发更改的错误。 我尝试了解问题的根源。当我学习可组合性时,我使用

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"
android kotlin android-jetpack-compose compose-recomposition
1个回答
0
投票

这可能是由于同时从不同线程更新 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

© www.soinside.com 2019 - 2024. All rights reserved.