Пару фактов о StateFlow и SharedFlow.
1) В отличии от простой реализации паттерна Observer, как это было в LiveData например, в StateFlow / SharedFlow механика основана на корутинах.
Всё начинается с подписки данных через collect, где запускается бесконечный цикл, который при отсутствии новых значений переводит корутину в SUSPEND состояние и что очень важно сохраняет ссылку на Continuation.
Думаю не секрет, что Continuation это обычный callback, который неявно передаётся в каждую suspend функцию и нужен для выхода из SUSPEND состояния.
При добавлении новых значений в StateFlow или SharedFlow извлекается сохранённый Continuation объект, у которого вызывается метод resume(), корутина выходит из SUSPEND состояния и подписчик получает новое значение или значения если это SharedFlow.
2) Состояния подписчиков, такие как ссылки на Continuation объекты, хранятся в массиве слотов AbstractSharedFlowSlot, по большей части это сделано для переиспользования общей логики, так как у StateFlow и SharedFlow общий родитель - AbstractSharedFlow.
3) Все возможные операции изменения состояния сделаны через атомарные конструкции (StateFlow) или synchronized блоки (SharedFlow), поэтому это потокобезопасные штуки.
4) StateFlow при изменении своего единственного значения сравнивает его с новым equals() методом, если значения равны, ничего не делает.
5) SharedFlow хранит значения в буфере, размер которого настраивается двумя параметрами: replay и extraBufferCapacity, первый отвечает за основную часть буфера, значения из этой части будут прилетать новым подписчикам, например если replay = 3, то при добавлении нового подписчика, в него придут 3 значения из буфера, второй параметр только добавляет дополнительное место для буферизации значений.
6) SharedFlow нельзя превратить в StateFlow если указать размер буфера нулевым, так как эта штука работает немного по другому, предположим у нас есть два подписчика и какой-нибудь источник в котором происходит генерация новых значений, все хорошо до тех пор пока один из подписчиков вдруг не окажется очень медленным и в случае нулевого размера буфера новое значение будет добавлено только когда все подписчики получат предыдущее, вызов SharedFlow.emit() в таком случае переходит в SUSPEND состояние.
Если буфер ненулевой то значения будут добавляться до тех пор пока он не заполнится.
7) Помимо перехода источников SharedFlow в SUSPEND состояние при переполнении буфера, есть ещё две интересные стратегии:
BufferOverflow.DROP_OLDEST - удаление из буфера самых старых значений
BufferOverflow.DROP_LATEST - пропуск новых значений, важно что здесь не происходит никаких операций с буфером
В обеих случаях SharedFlow.emit() успешно завершается и не переходит в SUSPEND состояние.
Пишите в комментах ваше мнение и всем хорошего кода!
Post #50
1.79K

- 👍 21
- 🔥 5