SharedFlow предоставляет более универсальный вариант потоковой передачи, чем StateFlow
. Ключевые различия между StateFlow и SharedFlow
заключаются в следующем:
При создании объекта SharedFlow начальное значение не указывается
SharedFlow позволяет получить значения, которые были отправлены до начала сбора данных
SharedFlow генерирует значения с помощью emit()
вместо использования свойства value
Для создания объекта SharedFlow сначала вызывается функция MutableSharedFlow(), которая создает значение типа MutableSharedFlow:
public fun <T> MutableSharedFlow( replay: Int = 0, extraBufferCapacity: Int = 0, onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND ): MutableSharedFlow<T>
Эта функция принимает три параметра:
replay
: количество значений, воспроизводимых новым подписчикам
extraBufferCapacity
: количество значений, буферизуемых в дополнение к значению параметра replay
.
emit не приостанавливается, пока остается свободное пространство в буфере.
onBufferOverflow
: настраивает действие отправки при переполнении буфера. Значения, отличные от BufferOverflow.SUSPEND, поддерживаются только в том случае,
если replay больше 0 или extraBufferCapacity больше 0. Переполнение буфера может произойти только в том случае, если есть хотя бы один подписчик, который не готов принять новое значение.
При отсутствии подписчиков сохраняются только самые последние значения воспроизведения, а поведение переполнения буфера никогда не срабатывает и не имеет никакого эффекта.
Может принимать следующие значения:
DROP_LATEST
: последнее значение удаляется, когда буфер заполнен, оставляя буфер неизменным при обработке новых значений.
DROP_OLDEST
: рассматривает буфер как стек, где самое старое значение отбрасывается, чтобы освободить место для нового значения, когда буфер заполнен.
SUSPEND
: поток приостанавливается, когда буфер заполнен.
После создания у значения MutableSharedFlow вызывается метод asSharedFlow() для получения ссылки SharedFlow.
private val _sharedFlow = MutableSharedFlow<Int>() val sharedFlow = _sharedFlow.asSharedFlow()
Значения передаются в поток SharedFlow путем вызова метода emit() объекта MutableSharedFlow:
someCoroutineScope.launch { for (i in 1..5) { _sharedFlow.emit(i) } }
Например, возьмем пример с StateFlow из прошлой статьи и изменим его, заменив StateFlow на SharedFlow:
package com.example.helloapp import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview import androidx.compose.material3.Text import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { val _sharedFlow = MutableSharedFlow<Int>() val sharedFlow = _sharedFlow.asSharedFlow() val coroutineScope = rememberCoroutineScope() LaunchedEffect(Unit) { coroutineScope.launch() { for (i in 1..5) { _sharedFlow.emit(i) delay(2000) } } } Main(flow=sharedFlow) } } } @Composable fun Main(flow: SharedFlow<Int>) { val count by flow.collectAsState(0) val messages = remember { mutableStateListOf<Int>()} LaunchedEffect(Unit) { flow.collect { messages.add(it) } } Column(Modifier.fillMaxSize()) { Text("$count", fontSize = 44.sp) LazyColumn { items(messages) { Text("Collected Value = $it",Modifier.padding(5.dp)) } } } }
Здесь вначале собственно определяем состояние SharedFlow:
val _sharedFlow = MutableSharedFlow<Int>() val sharedFlow = _sharedFlow.asSharedFlow()
Для эммитирования значений в поток определяем контекст корутины:
val coroutineScope = rememberCoroutineScope()
И чтобы при запуске приложения началась генерация значений в поток, определяем компонент LaunchedEffect:
LaunchedEffect(Unit) { coroutineScope.launch() { for (i in 1..5) { _sharedFlow.emit(i) delay(2000) } } }
В данном случае в поток поступают числа от 1 до 5 с задержкой в 2 секунды.
Для определения интерфейса определен компонент Main, который принимает поток SharedFlow. В этом компоненте сначала определяем состояние:
val count by flow.collectAsState(0) val messages = remember { mutableStateListOf<Int>()}
Переменная count получает текущее значение потока. А переменная messages хранит список полученных из потока значений. Для сбора значений в messages определен компонент LaunchedEffect:
LaunchedEffect(Unit) { flow.collect { messages.add(it) } }
Для отображения текущего значения из потока определен компонент Text, а для вывода всех полученных значений - компонент LazyList: