
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
// 3conflate
| 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
// 5flowOn
Изменяет контекст выполнения 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 4Emitters 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
// 4onStart
Выполняет заданное действие перед началом сбора данных из потока.
flowOf(1, 2, 3)
.onStart { emit(0) } // Добавляет 0 перед остальными элементами
.collect { println(it) } // Вывод: 0, 1, 2, 3onCompletion
Выполняет действие, когда поток завершается, независимо от того, завершился он успешно или из-за исключения.
flowOf(1, 2, 3)
.onCompletion { println("Completed") }
.collect { println(it) } // Вывод: 1, 2, 3, CompletedonEmpty
Позволяет указать действие, которое выполнится, если поток данных пуст. Часто используется для обработки случаев, когда поток не содержит элементов.
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
// 5dropWhile
Пропускает элементы потока, пока условие не станет ложным, и эмитирует оставшиеся.
val flow = flowOf(1, 2, 3, 4, 5)
val result = flow.dropWhile { it < 4 }
result.collect { println(it) }
// 4
// 5take
Ограничивает поток первым n элементами.
val flow = flowOf(1, 2, 3, 4, 5)
flow.take(3)
.collect { println(it) }
// 1
// 2
// 3takeWhile
Берет элементы из потока до тех пор, пока условие выполняется.
val flow = flowOf(1, 2, 3, 4, 5)
flow.takeWhile { it < 4 }
.collect { println(it) }
// 1
// 2
// 3transformWhile
Позволяет преобразовывать элементы потока, пока не выполнится заданное условие.
val flow = flowOf(1, 2, 3, 4)
flow.transformWhile { value ->
emit(value * 2)
value != 3 // прекращаем преобразование, если значение равно 3
}.collect { println(it) }
// Вывод:
// 2
// 4Merge Operators
flatMapConcat
Позволяет преобразовать элементы потока в новые потоки, обрабатывая их последовательно, один за другим.
val flow = flowOf(1, 2, 3)
flow
.flatMapConcat { value ->
flow {
emit(value * 2)
}
}
.collect { println(it) }
// Выведет: 2, 4, 6flatMapMerge
Позволяет преобразовать элементы потока в новые потоки и сливать их в один поток, обрабатывая параллельно.
val flow = flowOf(1, 2, 3)
flow
.flatMapMerge { value ->
flow {
emit(value * 2)
}
}
.collect { println(it) }
// Выведет: 2, 4, 6flatMapLatest
Позволяет преобразовывать элементы потока в новые потоки, при этом отменяя обработку предыдущих, если приходит новый элемент.
val flow = flowOf(1, 2, 3)
flow
.flatMapLatest { value ->
flow {
emit(value * 2)
}
}
.collect { println(it) }
// Выведет: 6merge
Объединяет несколько потоков, позволяя им испускать значения одновременно, сохраняя порядок их эмиссий.
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
// 6Transform Operators
filter
Пропускает элементы потока, не удовлетворяющие заданному условию, и эмитирует только те, которые удовлетворяют.
val flow = flowOf(1, 2, 3, 4, 5)
val result = flow.filter { it % 2 == 0 }
result.collect { println(it) }
// 2
// 4filterNot
Пропускает элементы потока, которые удовлетворяют заданному условию, и эмитирует только те, которые ему не соответствуют.
val flow = flowOf(1, 2, 3, 4, 5)
val result = flow.filterNot { it % 2 == 0 }
result.collect { println(it) }
// 1
// 3
// 5filterIsInstance
Фильтрует элементы потока, оставляя только те, которые могут быть приведены к указанному типу.
val flow = flowOf("Hello", 1, "World", 2)
val result = flow.filterIsInstance<String>()
result.collect { println(it) }
// Hello
// WorldfilterNotNull
Отфильтровывает все элементы, которые равны null, и эмитирует только ненулевые значения.
val flow = flowOf(1, null, 2, null, 3)
val result = flow.filterNotNull()
result.collect { println(it) }
// 1
// 2
// 3map
Применяет заданную функцию к каждому элементу потока и возвращает новый поток с результатами.
val flow = flowOf(1, 2, 3)
val result = flow.map { it * 2 }
result.collect { println(it) }
// 2
// 4
// 6mapNotNull
Применяет функцию к каждому элементу потока и фильтрует null значения из результата.
val flow = flowOf("1", "2", "a", "4")
val result = flow.mapNotNull { it.toIntOrNull() }
result.collect { println(it) }
// 1
// 2
// 4withIndex
Добавляет индекс к каждому элементу потока, превращая его в 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: conEach
Применяет функцию к каждому элементу потока, но не изменяет сам поток. Возвращает тот же поток для дальнейших операций.
val flow = flowOf(1, 2, 3)
flow.onEach { println("Value: $it") }
.collect()
// Value: 1
// Value: 2
// Value: 3scan
Накапливает значения, начиная с начального состояния, и эмитирует каждый промежуточный результат.
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
// 33combineTransform
Комбинирует несколько потоков и позволяет трансформировать их значения, эмитируя новые элементы на основе комбинации данных из всех потоков.
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
// 33zip
Позволяет комбинировать два потока, соединяя элементы в пары.
val flow1 = flowOf(1, 2, 3)
val flow2 = flowOf("A", "B", "C")
flow1.zip(flow2) { a, b -> "$a$b" }
.collect { println(it) }
// Вывод:
// 1A
// 2B
// 3CCollect 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) // 6fold
Применяет операцию к каждому элементу потока, начиная с начального значения, и возвращает накопленный результат.
val result = flowOf(1, 2, 3).fold(0) { acc, value -> acc + value }
println(result) // 6single
Возвращает единственный элемент из потока, либо бросает исключение, если элементов больше одного или поток пуст.
val result = flowOf(1).single()
println(result) // 1singleOrNull
Возвращает единственный элемент из потока или 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) // nullfirst
Возвращает первый элемент из потока, либо бросает исключение, если поток пуст.
val result = flowOf(1, 2, 3).first()
println(result) // 1firstOrNull
Возвращает первый элемент из потока или null, если поток пуст.
val result = flowOf<Int>().firstOrNull()
println(result) // nulllast
Возвращает последний элемент из потока, либо бросает исключение, если поток пуст.
val result = flowOf(1, 2, 3).last()
println(result) // 3lastOrNull
Возвращает последний элемент из потока или null, если поток пуст.
val result = flowOf<Int>().lastOrNull()
println(result) // nullCount Operators
count
Возвращает количество элементов в потоке.
val result = flowOf(1, 2, 3).count()
println(result) // 3Вопросы на собесе (16)
Operators (5)
- Какие есть типы функций над потоками?
В зависимости от того, возвращают они конкретное значение или обработанный поток функции делятся на: терминальные и промежуточные. Терминальные функции потоков представляют
suspend-функции, которые позволяют получать объекты из потока или возвращают какое-то конечное значение:collecttoListtoSetfirstfirstOrNulllastlastOrNullsinglesingleOrNullcountreducefold. Промежуточные функции не являютсяsuspend, принимают поток и возвращают обработанный поток:combinedropfilterfilterNotfilterNotNullmaponEachtaketransformzip.
- С какими операторами Flow работал?
combinedebouncefilterflatMapLatest
- Как работает оператор flowOn?
Оператор
flowOnв Kotlin изменяет контекст выполнения для операций эмиссии вFlow. Он позволяет выполнять длительные операции, такие как сетевые запросы или доступ к базе данных, в отдельном потоке, не блокируя основной поток. Это улучшает отзывчивость приложения и оптимизирует производительность.
- Как работает оператор collect?
Оператор
collectв Kotlin подписывается наFlowи получает эмитируемые значения, передавая каждый элемент в обработчик. Это асинхронный приостановленный метод, который можно использовать только в корутинах. Когда поток завершается,collectтакже завершает свою работу.
- Какие есть способы объединить 2 источника данных в Flow?
•
combineобъединяет последние значения из двухFlowи эмитит пары значений, когда один из них обновляется.•
zipобъединяетFlow, эмитируя пары значений, только когда оба источника испускают новые значения одновременно.
- Какие есть типы функций над потоками?
Другие (11)
- Для чего применяется Flow?
Flowиспользуется для асинхронной обработки последовательностей данных во времени, позволяя работать с потоками данных, которые могут изменяться и обновляться, обеспечивая реактивное программирование в Kotlin.
- Какие бывают виды Flow?
FlowStateFlowSharedFlow
- Как можно создавать Flow и какие есть билдеры?
•
flowэмитирует значения внутри корутины.•
flowOfсоздаетFlowиз заранее заданных значений.•
asFlowпреобразует коллекции или последовательности вFlow.•
channelFlowиспользуетChannelдля асинхронной эмиссии значений.•
callbackFlowсоздаетFlowкоторый работает с коллбэками.
- Какие операторы есть у Flow?
combinefilterdebouncetransformzip
- Какие источники 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?