Flow
https://developer.android.com/kotlin/flow |
https://developer.android.com/kotlin/flow/stateflow-and-sharedflow |
https://kotlinlang.org/docs/flow.html |
Flow Marbles: Interactive diagram of Kotlin Flow |
Flow
Асинхронный поток данных, который может эмитировать значения в корутинах. Он позволяет работать с последовательностями данных, предоставляя API для обработки событий, которые могут происходить со временем. Flow поддерживает операторы, такие как map, filter, и collect, что позволяет трансформировать и обрабатывать данные. Он также управляет потоком, обеспечивая безопасное выполнение в многопоточной среде.
SharedFlow
Поток данных, который используется для многократного вещания данных нескольким подписчикам. Он представляет собой горячий поток, что значит, что SharedFlow
не зависит от подписчиков и продолжает работать, даже если никто на него не подписан.
MutableSharedFlow
Позволяет эмиттировать значения в поток, делая его изменяемым.
class MyViewModel: ViewModel() {
private val _eventFlow = MutableSharedFlow<String>(replay = 1)
val eventFlow = _eventFlow.asSharedFlow()
fun sendEvent(event: String) {
viewModelScope.launch {
_eventFlow.emit(event) // Эмиттируем событие
}
}
}
replay
Определяет количество последних значений, которые будут сохраняться и отправляться новым подписчикам при их подключении. Например, если replay = 1
, каждый новый подписчик при подключении сразу получает последнее значение, даже если его эмиссия произошла до подписки. Это полезно для доставки критически важной информации, которая должна быть доступна всем подписчикам.
val sharedFlow = MutableSharedFlow<Int>(
replay = 1 // Хранит последнее значение для новых подписчиков
)
extraBufferCapacity
Указывает, сколько дополнительных значений можно отправить в MutableSharedFlow
, пока они ожидают получения подписчиками. Этот параметр добавляет буфер к значению replay
, позволяя временно хранить больше значений, если поток данных быстрее, чем подписчики могут их обрабатывать. Например, если replay = 1
и extraBufferCapacity = 2
, в буфере могут храниться три значения, что помогает избежать блокировки эмиттера при быстром потоке данных.
val sharedFlow = MutableSharedFlow<Int>(
extraBufferCapacity = 2 // Дополнительно хранит до 2 значений в буфере
)
onBufferOverflow
Определяет, что делать при переполнении буфера.
val sharedFlow = MutableSharedFlow<Int>(
onBufferOverflow = BufferOverflow.DROP_OLDEST // Удаляет самое старое значение при переполнении
)
BufferOverflow.SUSPEND
Приостанавливает эмиттер, пока в буфере не освободится место.
BufferOverflow.DROP_OLDEST
Удаляет самое старое значение в буфере, чтобы освободить место для нового.
BufferOverflow.DROP_LATEST
Отбрасывает новое значение, не добавляя его в буфер, если буфер заполнен.
asSharedFlow
Преобразует MutableSharedFlow
в неизменяемый SharedFlow
, предоставляя доступ только для подписки без возможности эмиссии значений.
private val _eventFlow: MutableSharedFlow<String> = MutableSharedFlow<String>()
val eventFlow: SharedFlow<String> = _eventFlow.asSharedFlow()
shareIn
Преобразует Flow
в SharedFlow
, делая его горячим и позволяя вещать значения одновременно нескольким подписчикам.
val sharedFlow: SharedFlow<Int> = flow
.shareIn(
scope = viewModelScope,
started = SharingStarted.Lazily,
replay = 0
)
scope
Определяет CoroutineScope
, в котором будет активен SharedFlow
. Этот параметр необходим для контроля жизненного цикла потока: SharedFlow
начнет испускать значения при активации scope и прекратит работу, когда scope завершится. В Android обычно используется viewModelScope
, чтобы поток был активен в течение жизни ViewModel
.
started
Определяет, когда поток начинает испускать значения.
SharingStarted.Eagerly
Немедленное начало эмиссии, независимо от подписчиков.
SharingStarted.Lazily
Эмиссия начнется только после появления первого подписчика.
SharingStarted.WhileSubscribed(replayExpirationMillis)
Поток начнет эмиссию при появлении подписчиков и остановится после указанного времени (replayExpirationMillis
), если все подписчики отписались. Это значение полезно для контроля ресурсов, так как поток приостанавливается, когда нет активных подписчиков.
replay
Указывает количество последних значений, которые сохраняются и передаются новым подписчикам, даже если они подключились после эмиссии. Например, если replay = 1
, новые подписчики сразу получают последнее значение. Этот параметр удобен, когда важные данные должны быть доступны новым подписчикам, независимо от момента их подключения.
tryEmit
Используется для попытки отправки значения в SharedFlow
без блокировки. Возвращает true, если значение успешно отправлено, или false, если подписчик не может принять значение (например, когда поток завершен). Это позволяет избежать выброса исключений при переполнении буфера.
val flow = MutableSharedFlow<String>()
val success = flow.tryEmit("Hello") // Вернет true, если подписчик активен
flow.collect { value ->
println(value)
}
StateFlow
Специальный тип потока, который предназначен для хранения и наблюдения за состоянием данных. Он является горячим потоком и всегда содержит одно текущее значение.
MutableStateFlow
Позволяет обновлять текущее значение состояния.
class MyViewModel: ViewModel() {
private val _stateFlow = MutableStateFlow("Начальное состояние")
val stateFlow: StateFlow<String> = _stateFlow
fun updateState(newState: String) {
_stateFlow.value = newState
}
}
value
Позволяет получить или установить текущее значение состояния.
compareAndSet
Используется для атомарного обновления значения. Он сравнивает текущее значение с ожидаемым, и если они совпадают, то устанавливает новое значение. Этот метод возвращает true
, если обновление произошло успешно, и false
— если текущее значение отличается от ожидаемого.
class MyViewModel: ViewModel() {
private val _stateFlow = MutableStateFlow(0)
val stateFlow: StateFlow<Int> = _stateFlow
// Увеличивает значение, если текущее значение совпадает с ожидаемым
fun incrementIfExpected(expected: Int) {
_stateFlow.compareAndSet(expected, expected + 1)
}
}
updateAndGet
Способ атомарного обновления значения с использованием переданной лямбды и получения нового значения после обновления. Он берет текущее значение, применяет к нему функцию-обработчик и устанавливает результат в MutableStateFlow
, возвращая новое значение.
class MyViewModel: ViewModel() {
private val _clickCount = MutableStateFlow(0)
val clickCount: StateFlow<Int> = _clickCount
// Увеличивает счетчик кликов и возвращает обновленное значение
fun incrementAndGetClickCount(): Int {
return _clickCount.updateAndGet { current -> current + 1 }
}
}
getAndUpdate
Атомарный метод, который позволяет сначала получить текущее значение MutableStateFlow
, а затем обновить его, применяя переданную функцию.
class MyViewModel: ViewModel() {
private val _clickCount = MutableStateFlow(0)
val clickCount: StateFlow<Int> = _clickCount
// Увеличивает счетчик и возвращает старое значение
fun incrementAndGetOldClickCount(): Int {
return _clickCount.getAndUpdate { current -> current + 1 }
}
}
update
Атомарный способ изменить текущее значение MutableStateFlow
с использованием переданной функции. В отличие от методов, которые возвращают старое или новое значение, update
просто применяет функцию обновления к текущему значению и устанавливает его в MutableStateFlow
, уведомляя подписчиков только если значение изменилось.
class MyViewModel: ViewModel() {
private val _clickCount = MutableStateFlow(0)
val clickCount: StateFlow<Int> = _clickCount
// Увеличивает счетчик кликов
fun incrementClickCount() {
_clickCount.update { current -> current + 1 }
}
}
asStateFlow
Преобразует изменяемый MutableStateFlow
в неизменяемый StateFlow
.
private val _stateFlow: MutableStateFlow<String> = MutableStateFlow("Начальное состояние")
val stateFlow: StateFlow<String> = _stateFlow.asStateFlow()
stateIn
Преобразует Flow
в StateFlow
, добавляя к нему текущее состояние. stateIn
делает поток горячим, то есть он начинает испускать значения, независимо от подписчиков, и всегда содержит одно актуальное значение, как StateFlow
.
val stateFlow: StateFlow<Int> = flow
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = 0
)
initialValue
Начальное значение, которое StateFlow
будет содержать до того, как Flow
начнет испускать данные. Это значение доступно сразу, даже если данные еще не поступили.
FlowCollector
emit
Используется для отправки значений из Flow
в его подписчиков. Является suspend
-функцией.
val flow = flow {
emit("Hello")
emit("World")
}
flow.collect { value ->
println(value) // Вывод: Hello, World
}
Builders
flow
Создает асинхронный поток данных.
val numbersFlow: Flow<Int> = flow {
for (i in 1..3) {
delay(1000)
emit(i)
}
}
asFlow
Преобразует коллекции и последовательности в Flow
.
val list = listOf(1, 2, 3)
val flowOfList: Flow<Int> = list.asFlow()
flowOf
Cоздает Flow
из одного или нескольких значений.
val numberFlow: Flow<Int> = flowOf(1, 2, 3, 5, 8)
channelFlow
Позволяет создать Flow
, используя каналы.
val flow: Flow<String> = channelFlow {
send("Hello")
send("World")
}
callbackFlow
Создает Flow
, который работает с коллбэками. Он оборачивает асинхронные вызовы, позволяя отправлять значения в поток, когда происходят определенные события.
val flow: Flow<Int> = callbackFlow {
val listener = object: MyListener {
override fun onValue(value: Int) {
trySend(value) // Отправляет значение в поток
}
}
registerListener(listener) // Регистрация слушателя
awaitClose { unregisterListener(listener) } // Удаление слушателя при закрытии
}
Context Operators
buffer
Позволяет накапливать элементы в буфере и передавать их потребителю, не ожидая, пока он обработает предыдущие. Это улучшает производительность, предотвращая блокировки.
val flow = flowOf(1, 2, 3)
flow
.buffer() // Используем буфер
.collect { value ->
delay(100) // Эмулируем длительную обработку
println(value)
}
// Выведет:
// 1
// 2
// 3
conflate
https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/conflate.html |
Позволяет пропускать промежуточные значения, если предыдущие еще не были обработаны. Это полезно, когда поток генерирует значения быстрее, чем можно их обработать, и нужно избежать задержек.
flowOf(1, 2, 3, 4, 5)
.conflate() // Пропускает промежуточные значения
.collect { value ->
delay(100) // Задержка для имитации обработки
println(value)
}
// 1
// 3
// 5
flowOn
Изменяет контекст выполнения Flow
. Используется для переключения на другой CoroutineDispatcher
. Например, можно использовать flowOn
для выполнения операций, которые требуют более длительного времени, в фоновом потоке, а затем возвращаться к основному потоку для сбора значений.
flow {
emit("Start")
delay(1000) // Долгая операция
emit("End")
}
.flowOn(Dispatchers.IO) // Переключаем на фоновый поток
.collect { value ->
println(value) // Собираем значения
}
Delay Operators
debounce
Позволяет игнорировать элементы потока, поступающие быстрее, чем указанный интервал времени.
val flow = flowOf(1, 2, 3, 4, 5)
flow
.debounce(100)
.collect { println(it) }
// Выведет последнее значение после паузы в 100 мс между элементами.
sample
Позволяет получать последнее значение потока через определённые интервалы времени.
val flow = flowOf(1, 2, 3, 4, 5)
flow
.sample(100)
.collect { println(it) }
// Выведет последнее значение, которое поступило за каждый 100 мс.
Distinct Operators
distinctUntilChanged
Пропускает только уникальные последовательные значения, игнорируя дубликаты.
flowOf(1, 2, 2, 3, 3, 3, 4)
.distinctUntilChanged()
.collect { println(it) } // Результат: 1 2 3 4
Emitters Operators
transform
Позволяет преобразовать элементы потока и эмитировать несколько значений для каждого входного элемента.
val flow = flowOf(1, 2, 3)
flow.transform { value ->
emit(value * 2) // Эмитирует удвоенное значение
emit(value + 1) // Эмитирует значение, увеличенное на 1
}.collect { println(it) }
// Вывод:
// 2
// 2
// 4
// 3
// 6
// 4
onStart
Выполняет заданное действие перед началом сбора данных из потока.
flowOf(1, 2, 3)
.onStart { emit(0) } // Добавляет 0 перед остальными элементами
.collect { println(it) } // Вывод: 0, 1, 2, 3
onCompletion
Выполняет действие, когда поток завершается, независимо от того, завершился он успешно или из-за исключения.
flowOf(1, 2, 3)
.onCompletion { println("Completed") }
.collect { println(it) } // Вывод: 1, 2, 3, Completed
onEmpty
Позволяет указать действие, которое выполнится, если поток данных пуст. Часто используется для обработки случаев, когда поток не содержит элементов.
val flow = emptyFlow<Int>()
flow
.onEmpty { println("Поток пустой") } // Действие при пустом потоке
.collect { value -> println(value) } // Поток пустой
Errors Operators
catch
Перехватывает ошибки в Flow
, позволяя продолжить выполнение после их обработки.
flow {
emit("Loading data")
throw Exception("Error")
}.catch { e ->
emit("Error: ${e.message}")
}.collect { value ->
println(value)
}
Limit Operators
drop
Пропускает первые N элементов потока и эмитирует оставшиеся.
val flow = flowOf(1, 2, 3, 4, 5)
val result = flow.drop(3)
result.collect { println(it) }
// 4
// 5
dropWhile
Пропускает элементы потока, пока условие не станет ложным, и эмитирует оставшиеся.
val flow = flowOf(1, 2, 3, 4, 5)
val result = flow.dropWhile { it < 4 }
result.collect { println(it) }
// 4
// 5
take
Ограничивает поток первым n элементами.
val flow = flowOf(1, 2, 3, 4, 5)
flow.take(3)
.collect { println(it) }
// 1
// 2
// 3
takeWhile
Берет элементы из потока до тех пор, пока условие выполняется.
val flow = flowOf(1, 2, 3, 4, 5)
flow.takeWhile { it < 4 }
.collect { println(it) }
// 1
// 2
// 3
transformWhile
Позволяет преобразовывать элементы потока, пока не выполнится заданное условие.
val flow = flowOf(1, 2, 3, 4)
flow.transformWhile { value ->
emit(value * 2)
value != 3 // прекращаем преобразование, если значение равно 3
}.collect { println(it) }
// Вывод:
// 2
// 4
Merge Operators
flatMapConcat
Позволяет преобразовать элементы потока в новые потоки, обрабатывая их последовательно, один за другим.
val flow = flowOf(1, 2, 3)
flow
.flatMapConcat { value ->
flow {
emit(value * 2)
}
}
.collect { println(it) }
// Выведет: 2, 4, 6
flatMapMerge
Позволяет преобразовать элементы потока в новые потоки и сливать их в один поток, обрабатывая параллельно.
val flow = flowOf(1, 2, 3)
flow
.flatMapMerge { value ->
flow {
emit(value * 2)
}
}
.collect { println(it) }
// Выведет: 2, 4, 6
flatMapLatest
Позволяет преобразовывать элементы потока в новые потоки, при этом отменяя обработку предыдущих, если приходит новый элемент.
val flow = flowOf(1, 2, 3)
flow
.flatMapLatest { value ->
flow {
emit(value * 2)
}
}
.collect { println(it) }
// Выведет: 6
merge
Объединяет несколько потоков, позволяя им испускать значения одновременно, сохраняя порядок их эмиссий.
val flow1 = flowOf(1, 2)
val flow2 = flowOf(3, 4)
merge(flow1, flow2).collect { println(it) } // Вывод: 1, 2, 3, 4 (порядок зависит от исполнения)
mapLatest
Преобразует значения, пропуская результаты предыдущих преобразований, если поступает новое значение.
flowOf(1, 2, 3)
.mapLatest { value ->
delay(100) // Длительная операция
"Transformed $value"
}
.collect { println(it) } // Вывод: Transformed 3 (1 и 2 прерываются)
transformLatest
Позволяет преобразовывать элементы потока, при этом если приходит новый элемент, предыдущая операция отменяется.
val flow = flowOf(1, 2, 3)
flow.transformLatest { value ->
emit(value * 2)
delay(100) // имитация долгой работы
emit(value + 1)
}.collect { println(it) }
// Вывод:
// 2
// 3
// 6
Transform Operators
filter
Пропускает элементы потока, не удовлетворяющие заданному условию, и эмитирует только те, которые удовлетворяют.
val flow = flowOf(1, 2, 3, 4, 5)
val result = flow.filter { it % 2 == 0 }
result.collect { println(it) }
// 2
// 4
filterNot
Пропускает элементы потока, которые удовлетворяют заданному условию, и эмитирует только те, которые ему не соответствуют.
val flow = flowOf(1, 2, 3, 4, 5)
val result = flow.filterNot { it % 2 == 0 }
result.collect { println(it) }
// 1
// 3
// 5
filterIsInstance
Фильтрует элементы потока, оставляя только те, которые могут быть приведены к указанному типу.
val flow = flowOf("Hello", 1, "World", 2)
val result = flow.filterIsInstance<String>()
result.collect { println(it) }
// Hello
// World
filterNotNull
Отфильтровывает все элементы, которые равны null
, и эмитирует только ненулевые значения.
val flow = flowOf(1, null, 2, null, 3)
val result = flow.filterNotNull()
result.collect { println(it) }
// 1
// 2
// 3
map
Применяет заданную функцию к каждому элементу потока и возвращает новый поток с результатами.
val flow = flowOf(1, 2, 3)
val result = flow.map { it * 2 }
result.collect { println(it) }
// 2
// 4
// 6
mapNotNull
Применяет функцию к каждому элементу потока и фильтрует null
значения из результата.
val flow = flowOf("1", "2", "a", "4")
val result = flow.mapNotNull { it.toIntOrNull() }
result.collect { println(it) }
// 1
// 2
// 4
withIndex
Добавляет индекс к каждому элементу потока, превращая его в IndexedValue
.
flowOf("a", "b", "c")
.withIndex()
.collect { println("Index: ${it.index}, Value: ${it.value}") }
// Вывод:
// Index: 0, Value: a
// Index: 1, Value: b
// Index: 2, Value: c
onEach
Применяет функцию к каждому элементу потока, но не изменяет сам поток. Возвращает тот же поток для дальнейших операций.
val flow = flowOf(1, 2, 3)
flow.onEach { println("Value: $it") }
.collect()
// Value: 1
// Value: 2
// Value: 3
scan
Накапливает значения, начиная с начального состояния, и эмитирует каждый промежуточный результат.
flowOf(1, 2, 3)
.scan(0) { accumulator, value -> accumulator + value }
.collect { println(it) }
// Вывод:
// 0 (начальное значение)
// 1 (0 + 1)
// 3 (1 + 2)
// 6 (3 + 3)
runningReduce
Накапливает значения, эмитируя промежуточные результаты, но без начального значения.
flowOf(1, 2, 3)
.runningReduce { accumulator, value -> accumulator + value }
.collect { println(it) }
// Вывод:
// 1
// 3 (1 + 2)
// 6 (3 + 3)
runningFold
Накапливает значения, начиная с заданного начального значения, и эмитирует каждый промежуточный результат.
flowOf(1, 2, 3)
.runningFold(0) { accumulator, value -> accumulator + value }
.collect { println(it) }
// Вывод:
// 0 (начальное значение)
// 1 (0 + 1)
// 3 (1 + 2)
// 6 (3 + 3)
chunked
https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/chunked.html |
Разбивает заданный поток на поток непересекающихся списков, каждый из которых не превышает заданный размер, но никогда не бывает пустым. Последний эмитируемый список может содержать меньше элементов, чем заданный размер.
flowOf("a", "b", "c", "d", "e")
.chunked(2) // ["a", "b"], ["c", "d"], ["e"]
.map { it.joinToString(separator = "") }
.collect {
println(it) // Prints "ab", "cd", e"
}
Zip Operators
combine
Комбинирует несколько потоков, эмитируя новые элементы, каждый раз, когда хотя бы один из потоков эмитирует новое значение.
val flow1 = flowOf(1, 2, 3)
val flow2 = flowOf(10, 20, 30)
val result = flow1.combine(flow2) { a, b -> a + b }
result.collect { println(it) }
// 11
// 22
// 33
combineTransform
Комбинирует несколько потоков и позволяет трансформировать их значения, эмитируя новые элементы на основе комбинации данных из всех потоков.
val flow1 = flowOf(1, 2, 3)
val flow2 = flowOf(10, 20, 30)
val result = flow1.combineTransform(flow2) { a, b ->
emit(a + b) // Можно применить любую логику трансформации
}
result.collect { println(it) }
// 11
// 22
// 33
zip
Позволяет комбинировать два потока, соединяя элементы в пары.
val flow1 = flowOf(1, 2, 3)
val flow2 = flowOf("A", "B", "C")
flow1.zip(flow2) { a, b -> "$a$b" }
.collect { println(it) }
// Вывод:
// 1A
// 2B
// 3C
Collect Operators
collect
Используется для сбора значений, эмитируемых потоком. Запускает сбор значений и выполняет заданное действие для каждого полученного значения.
• Блокирует текущий поток до тех пор, пока все данные не будут собраны или поток не завершится.
val flow = flowOf(1, 2, 3)
flow.collect { value ->
println(value)
}
collectIndexed
Собирает значения из Flow
и предоставляет индекс каждого элемента в коллекторе.
flowOf("a", "b", "c")
.collectIndexed { index, value ->
println("Index: $index, Value: $value")
}
collectLatest
Cобирает значения, но отменяет обработку предыдущего значения, если новое значение приходит до завершения обработки. Это полезно для случаев, когда только последние значения имеют значение, например, в UI, где пользователь может вводить текст.
val flow = flow {
emit("First")
delay(100) // Имитация задержки
emit("Second")
}
flow.collectLatest { value ->
println(value) // Напечатает только "Second"
}
collectAsState
Собирает данные из Flow
или StateFlow
и превращает их в State
. Обновляет UI автоматически, когда поток отправляет новые данные.
class MyViewModel: ViewModel() {
private val _stateFlow = MutableStateFlow("Hello, Compose!")
val stateFlow: StateFlow<String> = _numberFlow.asStateFlow()
}
@Composable
fun ExampleScreen(viewModel: MyViewModel) {
val uiState by viewModel.stateFlow.collectAsState()
Text(text = "State: $uiState")
}
collectAsStateWithLifecycle
Собирает данные из Flow
или StateFlow
, учитывая жизненный цикл Composable
. Автоматически приостанавливает сбор, когда UI неактивен, и возобновляет, когда UI снова в фокусе.
class MyViewModel: ViewModel() {
private val _stateFlow = MutableStateFlow("Hello, Compose!")
val stateFlow = _stateFlow.asStateFlow()
}
@Composable
fun ExampleScreen(viewModel: MyViewModel) {
val uiState by viewModel.stateFlow.collectAsStateWithLifecycle()
Text(text = "State: $uiState")
}
Collection Operators
toList
Преобразует элементы Flow
в список List
.
val numbersFlow: Flow<Int> = flowOf(1, 2, 3, 4, 5)
runBlocking {
val numbersList: List<Int> = numbersFlow.toList()
println(numbersList) // [1, 2, 3, 4, 5]
}
toSet
Преобразует элементы Flow
в Set
, собирая уникальные значения из потока. Игнорирует дубликаты.
val numbersFlow: Flow<Int> = flowOf(1, 2, 2, 3, 4, 5, 5)
runBlocking {
val numbersSet: Set<Int> = numbersFlow.toSet()
println(numbersSet) // [1, 2, 3, 4, 5]
}
toCollection
Преобразует элементы Flow
в заданную коллекцию, например, ArrayList
или LinkedList
. Позволяет настраивать тип коллекции, в которую будут собираться элементы.
val numbersFlow: Flow<Int> = flowOf(1, 2, 3, 4)
runBlocking {
val numbersList: ArrayList<Int> = numbersFlow.toCollection(ArrayList())
println(numbersList) // [1, 2, 3, 4]
}
Reduce Operators
reduce
Применяет операцию к каждому элементу потока, аккумулируя результат.
val result = flowOf(1, 2, 3).reduce { acc, value -> acc + value }
println(result) // 6
fold
Применяет операцию к каждому элементу потока, начиная с начального значения, и возвращает накопленный результат.
val result = flowOf(1, 2, 3).fold(0) { acc, value -> acc + value }
println(result) // 6
single
Возвращает единственный элемент из потока, либо бросает исключение, если элементов больше одного или поток пуст.
val result = flowOf(1).single()
println(result) // 1
singleOrNull
Возвращает единственный элемент из потока или null
, если поток пуст или содержит больше одного элемента.
val result = flowOf(1).singleOrNull()
println(result) // 1
val resultEmpty = flowOf<Int>().singleOrNull()
println(resultEmpty) // null
val resultMultiple = flowOf(1, 2).singleOrNull()
println(resultMultiple) // null
first
Возвращает первый элемент из потока, либо бросает исключение, если поток пуст.
val result = flowOf(1, 2, 3).first()
println(result) // 1
firstOrNull
Возвращает первый элемент из потока или null
, если поток пуст.
val result = flowOf<Int>().firstOrNull()
println(result) // null
last
Возвращает последний элемент из потока, либо бросает исключение, если поток пуст.
val result = flowOf(1, 2, 3).last()
println(result) // 3
lastOrNull
Возвращает последний элемент из потока или null
, если поток пуст.
val result = flowOf<Int>().lastOrNull()
println(result) // null
Count Operators
count
Возвращает количество элементов в потоке.
val result = flowOf(1, 2, 3).count()
println(result) // 3
Вопросы на собесе (16)
Operators (5)
- Какие есть типы функций над потоками?
В зависимости от того, возвращают они конкретное значение или обработанный поток функции делятся на: терминальные и промежуточные. Терминальные функции потоков представляют
suspend
-функции, которые позволяют получать объекты из потока или возвращают какое-то конечное значение:collect
toList
toSet
first
firstOrNull
last
lastOrNull
single
singleOrNull
count
reduce
fold
. Промежуточные функции не являютсяsuspend
, принимают поток и возвращают обработанный поток:combine
drop
filter
filterNot
filterNotNull
map
onEach
take
transform
zip
.
- С какими операторами Flow работал?
combine
debounce
filter
flatMapLatest
- Как работает оператор flowOn?
Оператор
flowOn
в Kotlin изменяет контекст выполнения для операций эмиссии вFlow
. Он позволяет выполнять длительные операции, такие как сетевые запросы или доступ к базе данных, в отдельном потоке, не блокируя основной поток. Это улучшает отзывчивость приложения и оптимизирует производительность.
- Как работает оператор collect?
Оператор
collect
в Kotlin подписывается наFlow
и получает эмитируемые значения, передавая каждый элемент в обработчик. Это асинхронный приостановленный метод, который можно использовать только в корутинах. Когда поток завершается,collect
также завершает свою работу.
- Какие есть способы объединить 2 источника данных в Flow?
•
combine
объединяет последние значения из двухFlow
и эмитит пары значений, когда один из них обновляется.•
zip
объединяетFlow
, эмитируя пары значений, только когда оба источника испускают новые значения одновременно.
- Какие есть типы функций над потоками?
Другие (11)
- Для чего применяется Flow?
Flow
используется для асинхронной обработки последовательностей данных во времени, позволяя работать с потоками данных, которые могут изменяться и обновляться, обеспечивая реактивное программирование в Kotlin.
- Какие бывают виды Flow?
Flow
StateFlow
SharedFlow
- Как можно создавать Flow и какие есть билдеры?
•
flow
эмитирует значения внутри корутины.•
flowOf
создаетFlow
из заранее заданных значений.•
asFlow
преобразует коллекции или последовательности вFlow
.•
channelFlow
используетChannel
для асинхронной эмиссии значений.•
callbackFlow
создаетFlow
который работает с коллбэками.
- Какие операторы есть у Flow?
combine
filter
debounce
transform
zip
- Какие источники Flow считаются горячими?
StateFlow
иSharedFlow
- Разница между холодным и горячим потоками в Kotlin?
Холодный поток в Kotlin создается с помощью
Flow
, который начинает эмитировать данные только при подписке. Горячий поток реализуется с помощьюSharedFlow
илиStateFlow
, который продолжает эмитировать данные независимо от подписчиков.
- Как из холодного потока сделать горячий?
Использовать операторы
stateIn
иshareIn
.
- Разница между StateFlow и SharedFlow?
•
StateFlow
хранит текущее значение и всегда возвращает последнее значение новым подписчикам, обеспечивая сохранение состояния.•
SharedFlow
не сохраняет состояние, а просто транслирует эмитированные значения всем подписчикам, без сохранения их в истории.
- Как обрабатывать ошибки в Flow?
•
catch
перехватывает исключения и позволяет выполнить альтернативные действия.•
onCompletion
выполняет действия при завершении потока, независимо от результата.•
retry
позволяет повторить операции в случае ошибки.
- Для чего используется SharedFlow?
Для распространения данных между несколькими подписчиками, позволяя им получать последние значения и новые события из одного источника.
- Какие параметры есть у SharedFlow?
•
replay
количество последних значений, которые будут доступны новым подписчикам.•
extraBufferCapacity
количество дополнительных значений, которые могут быть помещены в буфер, когда все подписчики заняты. Это позволяет уменьшить количество пропущенных значений при высокой нагрузке.•
onBufferOverflow
поведение при переполнении буфера. Удалять старые значения при добавлении новых (BufferOverflow.DROP_OLDEST
) или новые ждут пока буфер освободится (BufferOverflow.SUSPEND
).
- Для чего применяется Flow?