Как исправить «Неподдерживаемое одновременное изменение во время композиции»?Android

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Как исправить «Неподдерживаемое одновременное изменение во время композиции»?

Сообщение Anonymous »

Ошибка:

Recomposer.applyAndCheck

java.lang.IllegalStateException — неподдерживаемое одновременное изменение во время композиции. Объект состояния был изменен в результате композиции, а также изменен вне композиции.

Время от времени у меня возникает ошибка с неподдерживаемым одновременным изменением.
Я пытаюсь понять источник проблемы. Когда я изучал компоновку, я использовал coroutineScope с Dispatchers.IO для обновления состояния, и это вызывало ту же проблему. Я прочитал https://developer.android.com/develop/u ... de-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))
}
}
}

Это автоматический слайдер, мне пришлось обойти его таким образом из-за перехода от аккомпаниатора к более новому подходу.
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState

const val foundation = "androidx.compose.foundation:foundation:1.6.1"

EDIT1:
private var _homeState = MutableStateFlow(HomeState(isAddressAddedFromOnboarding))
val homeState: StateFlow = _homeState
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(4000L),
initialValue = HomeState(isAddressAddedFromOnboarding)
)

private fun checkIsActiveOrder() {
viewModelScope.launch {
isActiveOrderInDeliveryInteractor.invoke().collectLatest {
_homeState.value = _homeState.value.copy(isActiveOrder = it)
}
}
}

Or with val _homeState
_homeState.update {
copy(isNotificationPermissionAsked = it)
}


Как вы предлагаете обновить эти данные? У меня много таких коллекционеров. Вы предлагаете объединить около 10 ~? Могу ли я просто скопировать и собрать его, как раньше? Но с новым подходом HomeScreen.
Я использую расширение:
infix fun MutableStateFlow.update(updater: T.() -> T) {
this.value = updater(this.value)
}

EDIT2:
[...]
: ViewModel() {

private val _homeState = MutableStateFlow(HomeState(isAddressAddedFromOnboarding))
val homeState: StateFlow = _homeState.asStateFlow()

init {
viewModelScope.launch {
combine(
getAccountTypeInteractor.invoke(),
getDeliveryEstimatedTimeInteractor.invoke(),
getInventoryInteractor.invoke(),
getSliderItemsInteractor.invoke(),
getBestSellersProductsInteractor.invoke(),
getSpecialCategoriesInteractor.invoke(),
getCurrentAddressInteractor.invoke(),
getVerificationAgeStateInteractor.invoke(),
getIsNewDarkStoreSyncInteractor.invoke(),
getAdditionalMenuOptionsInteractor.invoke(),
getBasketTotalValueWithoutDiscountInteractor.invoke(),
checkIsStoriesDataLoadedInteractor()
) { accountType, deliveryEstimatedTime, inventory, sliders, bestSellers, specialCategories, currentAddress, ageVerificationState, isLoading, additionalMenuOptions, basketTotalValueWithoutDiscount, storiesDataLoaded, ->

// it doesn't log anything, not even null
Log.d("homeStateVALUES", """
accountType: $accountType,
deliveryEstimatedTime: $deliveryEstimatedTime,
inventory: $inventory,
sliders: $sliders,
bestSellers: $bestSellers,
specialCategories: $specialCategories,
currentAddress: $currentAddress,
ageVerificationState: $ageVerificationState,
isLoading: $isLoading,
additionalMenuOptions: $additionalMenuOptions,
basketTotalValueWithoutDiscount: $basketTotalValueWithoutDiscount,
storiesDataLoaded: $storiesDataLoaded
""")

HomeState(
isAddressAddedFromOnboarding = isAddressAddedFromOnboarding,
isLogged = accountType == AccountType.STANDARD_ACCOUNT,
chipTime = deliveryEstimatedTime,
categories = inventory.map {
it.copy(subCategories = emptyList())
},
sliderItems = sliders,
bestsellers = bestSellers,
specialCategories = specialCategories,
currentAddress = currentAddress,
ageVerificationState = ageVerificationState,
isLoading = isLoading,
additionalMenuOptions = additionalMenuOptions,
basketTotalValueWithoutDiscount = basketTotalValueWithoutDiscount,
storiesDataLoaded = storiesDataLoaded
)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(1000L),
initialValue = _homeState.value
)
}
}

onTriggeredEvent~
is HomeEvent.OnFreeCouponAccepted -> {
viewModelScope.launch {
_homeState.updateFlow {
copy(isAddressAddedFromOnboarding = false)
}
}
}

HomeScreen
fun HomeScreen(
state: StateFlow // I'm using here viewModel.homeState
) {

val homeState by state.collectAsStateWithLifecycle()

Здесь это не работает. Я не могу загрузить свои данные.
Edit3:
@Composable
fun HomeScreen() {
val scaffoldState = rememberScaffoldState()
val screenHeightDp = LocalConfiguration.current.screenHeightDp.dp
val screenWidthDp = LocalConfiguration.current.screenWidthDp.dp
val screenHeight by remember { mutableStateOf(screenHeightDp) }
val screenWidth by remember { mutableStateOf(screenWidthDp) }

val listState = rememberLazyListState()
val coroutineScope = rememberCoroutineScope()
[...]

@Composable
fun DoubleBackToLeaveApp(drawerState: DrawerState) {
val doubleBackToExitPressedOnce = remember { mutableStateOf(true) }
val backScope = rememberCoroutineScope()
val backContext = LocalContext.current

BackHandler(doubleBackToExitPressedOnce.value) {
[...]

Компонуемый элемент, используемый в HomeScreen
@Composable
fun DoubleBackToLeaveApp(drawerState: DrawerState) {
val doubleBackToExitPressedOnce = remember { mutableStateOf(true) }
val backScope = rememberCoroutineScope()
val backContext = LocalContext.current

Этот пример неправильный? Может ли это вызвать эту ошибку? Здесь состояние создается в компонуемой форме, и в то же время у меня есть логика в viewModel с моим внешним состоянием.
Должен ли я изменить ее на:
@Composable
fun DoubleBackToLeaveApp(
drawerState: DrawerState,
doubleBackToExitPressedOnce: MutableState, // take care in my HomeState as other params?
backScope: CoroutineScope
) {
val backContext = LocalContext.current

Еще один пример, здесь по-вашему неправильно? isAsked создается в @Composable, поэтому мне снова следует использовать HomeState и доставить его этому компоненту, верно?
@OptIn(ExperimentalComposeUiApi::class, ExperimentalPermissionsApi::class)
@Composable
fun TurnNotificationOn(
onFinish: () -> Unit
) {
val NotificationPermissionState: PermissionState
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
notificationPermissionState = rememberPermissionState(
Manifest.permission.POST_NOTIFICATIONS
)
} else {
onFinish()
return
}

val isAsked = remember { mutableStateOf(false) }

LaunchedEffect(notificationPermissionState.status) {
if (isAsked.value) {
onFinish()
}
isAsked.value = true
}



Подробнее здесь: https://stackoverflow.com/questions/785 ... omposition
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»