Что такое неконсистентное состояние
Что проверяют
Умеете ли вы рассуждать о состоянии экрана как о едином значении. В Android это не теория: состояние собирается из нескольких асинхронных источников, экран пересоздаётся при повороте и восстанавливается после смерти процесса, а вопрос в том, видите ли вы, где сочетание значений становится невозможным.
Как отвечать
Неконсистентное состояние это набор значений, который вместе противоречит правилам домена. Индикатор загрузки крутится, и тут же показана ошибка. Список пуст, флаг загрузки уже снят, и пользователь видит «ничего не найдено» вместо данных. Счётчик корзины показывает одно, а содержимое другое.
Откуда берётся в Android: состояние размазано по нескольким независимым полям, и каждое обновляется своей корутиной; ответы на два запроса приходят в порядке, обратном порядку отправки; экран пересоздан, часть значений восстановлена, часть нет; база и сервер разошлись после неудачной отправки.
Лечится структурой, а не замками:
data class ScreenState(
val items: List<Item> = emptyList(),
val loading: Boolean = false,
val error: Throwable? = null,
)
private val _state = MutableStateFlow(ScreenState())
fun reload() = viewModelScope.launch {
_state.update { it.copy(loading = true, error = null) }
}Одно неизменяемое значение вместо россыпи полей, обновление через update, то есть атомарное сравнение с обменом. Устаревшие ответы отсекает оператор вроде flatMapLatest, единственным источником правды делается база, а то, что должно пережить смерть процесса, уезжает в SavedStateHandle.
Где спотыкаются
Пишут _state.value = _state.value.copy(...). Это чтение, изменение и запись тремя шагами: две корутины сделают это одновременно, и одно обновление молча потеряется.
Держат загрузку, ошибку и данные тремя отдельными потоками. Невозможные сочетания тогда не запрещены типом, а ловятся глазами тестировщика, тогда как запечатанный класс состояний убирает проблему на уровне компиляции.
Считают, что synchronized закрывает тему. Гонка чаще не за память, а за порядок: пользователь дважды дёрнул обновление, ответы вернулись наоборот, и на экране старые данные. Спасает отмена прежней работы, а не замок.