Как сделать данные иммутабельными в Jetpack Compose
Что проверяют
Понимаете ли вы, как иммутабельность связана со стабильностью в Compose и пропуском рекомпозиции, а не только знаете про copy(). Следом обычно спрашивают, почему компонент перерисовывается при каждом изменении экрана, хотя его данные не менялись.
Как отвечать
Состояние экрана описывают неизменяемым классом, а меняют заменой целиком:
- только
val:data classсам по себе ничего не гарантирует, одно полеvarилиMutableListвнутри делает класс изменяемым и нестабильным для компилятора Compose; - неизменяемые коллекции: интерфейс
Listкомпилятор считает нестабильным, под ним может оказатьсяArrayList. Выход:ImmutableListиPersistentListизkotlinx.collections.immutableили обёртка с аннотацией; @Immutableи@Stable: обещание компилятору, что объект не меняется или сообщает об изменениях через снапшот-состояние. Если обещание нарушить, интерфейс перестанет обновляться;- обновление через копию: во ViewModel
MutableStateFlow<UiState>закрыт, наружу отдаётсяStateFlow, а изменения идут через_state.update { it.copy(isLoading = false) }. В UI поток собирают черезcollectAsStateWithLifecycle().
Зачем всё это: Compose пропускает функцию при рекомпозиции, если её параметры стабильны и равны прошлым. С Kotlin 2.0.20 режим strong skipping включён по умолчанию, и функции с нестабильными параметрами тоже пропускаются, но такие параметры сравниваются по ссылке. Изменяемый объект, поменянный на месте, ссылку не меняет, и экран покажет старые данные. Проверить стабильность классов помогают отчёты компилятора Compose, их включают в блоке composeCompiler в Gradle. Для классов из чужих модулей есть файл конфигурации стабильности.
Где спотыкаются
Говорят «data class значит неизменяемый». Неизменяемость дают val и неизменяемые типы полей, а не ключевое слово data.
Делают mutableStateOf(mutableListOf()) и вызывают add(). Ссылка та же, рекомпозиции нет. Нужен новый список (list + item) или mutableStateListOf().
Расставляют @Immutable на всё подряд, включая классы с изменяемыми полями, чтобы убрать предупреждения. Ошибка превращается в тихо не обновляющийся экран.