Как StateFlow помогает решить проблему синглтона
Что проверяют
Вопрос с подвохом: StateFlow не отменяет синглтон, а меняет то, как он отдаёт состояние. Интервьюер хочет услышать, что проблема глобального объекта в изменяемых полях, про изменения которых никто не узнаёт и которые пишут из разных потоков. Следом спросят, как такой объект тестировать и как собирать поток без утечек.
Как отвечать
- в чём проблема: классический
object UserSession { var user: User? }хранит состояние молча. Экранам приходится опрашивать его или регистрировать слушателей и не забывать отписаться, иначе синглтон держит ссылку на Activity; - что даёт StateFlow:
MutableStateFlowвсегда хранит текущее значение, новый подписчик сразу получает последнее, повтор равного значения не эмитится (сравнение черезequals). Изменение черезupdate { it.copy(...) }атомарно, гонки при конкурентной записи нет; - инкапсуляция: наружу отдаётся
StateFlowчерезasStateFlow(), менять состояние может только владелец; - жизненный цикл: подписку контролирует тот, кто подписался. Во View это
repeatOnLifecycle(Lifecycle.State.STARTED), в ComposecollectAsStateWithLifecycle(). Экран уходит, корутина отменяется, ссылок на него в синглтоне не остаётся; - вместо ручного
object: репозиторий с аннотацией@Singletonв Hilt держитStateFlow, а ViewModel превращает его в состояние экрана черезstateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), initial). Единственность экземпляра обеспечивает граф зависимостей, поэтому в тестах подставляется фейк, а поток проверяется черезrunTestи Turbine.
Итог: синглтон остаётся, но становится внедряемым источником наблюдаемого состояния.
Где спотыкаются
Утверждают, что StateFlow сам знает о жизненном цикле и чистит ресурсы. Не знает: lifecycleScope.launch { flow.collect {} } без repeatOnLifecycle продолжит собирать и в фоне.
Кладут в StateFlow одноразовые события вроде навигации или тоста. Повтор равного значения проглатывается, а после поворота экрана событие придёт снова.
Мутируют объект внутри value, например добавляют элемент в тот же MutableList. Ссылка прежняя, equals возвращает true, и подписчики ничего не получают.