Как обновлять MutableStateFlow в Kotlin без потери изменений

Как обновлять MutableStateFlow в Kotlin без потери изменений
Содержание статьи

Две корутины могут прочитать один и тот же счётчик и записать одинаковое новое значение. Одно увеличение тогда потеряется. MutableStateFlow.update позволяет атомарно применить изменение к текущему состоянию. На Mentoring.digital рассмотрим это на небольшом состоянии экрана.

Где возникает потерянное обновление

Выражение state.value = state.value.copy(...) читает и записывает значение отдельными операциями. Допустим, обе корутины видят count = 3. Каждая вычисляет 4, и итог остаётся 4 вместо ожидаемых 5. Это возможно, даже когда само чтение и сама запись потокобезопасны.

Обновление через update

import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update

data class ScreenState(val count: Int = 0)

class CounterState {
    private val mutable = MutableStateFlow(ScreenState())
    val state = mutable.asStateFlow()

    fun increment() {
        mutable.update { old ->
            old.copy(count = old.count + 1)
        }
    }
}

Метод вычисляет новое значение из актуального состояния. Снаружи доступен поток для чтения. Внутри лямбды остаётся вычисление, поскольку при конкурирующих изменениях update может выполнить её больше одного раза. Запись в базу, отправку события и другие побочные действия выполняйте отдельно.

Как проверить параллельные обновления

В тесте запустите несколько корутин, которые вызывают increment(), дождитесь их завершения и сравните конечный count с числом вызовов. Подписчик StateFlow может пропустить промежуточные значения, поэтому для этой задачи сравнивайте финальное состояние. Если нужно обновлять сразу несколько связанных полей, поместите их в один объект состояния и меняйте одним вызовом.

Состояние экрана можно отработать на курсе по Android-разработке. Если в реальном проекте есть несколько источников обновлений, разберите их порядок с наставником по Kotlin.

Похожие статьи по данной теме