StateFlow und SharedFlow sind Flow APIs, mit denen Flows Statusaktualisierungen optimal und Werte an mehrere Consumer senden können.
StateFlow
StateFlow ist ein beobachtbarer Flow, der den aktuellen und neuen Status an seine Collector ausgibt. Der aktuelle Statuswert kann auch über die Property value gelesen werden. Wenn Sie den Status aktualisieren und an den Ablauf senden möchten, weisen Sie der value-Property der Klasse MutableStateFlow einen neuen Wert zu.
In Android ist StateFlow eine gute Wahl für Klassen, die einen beobachtbaren, veränderlichen Zustand beibehalten müssen.
Gemäß den Beispielen aus Kotlin-Flows kann ein StateFlow über das LatestNewsViewModel bereitgestellt werden, sodass das View auf Aktualisierungen des UI-Status warten kann und der Bildschirmstatus Konfigurationsänderungen übersteht.
class LatestNewsViewModel( private val newsRepository: NewsRepository ) : ViewModel() { // Backing property to avoid state updates from other classes private val _uiState = MutableStateFlow(LatestNewsUiState.Success(emptyList())) // The UI collects from this StateFlow to get its state updates val uiState: StateFlow<LatestNewsUiState> = _uiState init { viewModelScope.launch { newsRepository.favoriteLatestNews // Update UI with the latest favorite news // Writes to the value property of MutableStateFlow, // adding a new element to the flow and updating all // of its collectors .collect { favoriteNews -> _uiState.value = LatestNewsUiState.Success(favoriteNews) } } } } // Represents different states for the LatestNews screen sealed class LatestNewsUiState { data class Success(val news: List<ArticleHeadline>) : LatestNewsUiState() data class Error(val exception: Throwable) : LatestNewsUiState() }
Die Klasse, die für die Aktualisierung eines MutableStateFlow verantwortlich ist, ist der Producer. Alle Klassen, die Daten aus dem StateFlow abrufen, sind die Consumer. Im Gegensatz zu einem kalten Flow, der mit dem flow-Builder erstellt wurde, ist ein StateFlow heiß: Beim Erfassen aus dem Flow wird kein Erstellercode ausgelöst. Ein StateFlow ist immer aktiv und im Arbeitsspeicher. Es kommt erst für die Garbage Collection infrage, wenn keine anderen Referenzen darauf von einem Garbage Collection-Root vorhanden sind.
Wenn ein neuer Consumer mit dem Erfassen von Daten aus dem Stream beginnt, empfängt er den letzten Status im Stream und alle nachfolgenden Status. Dieses Verhalten ist auch in anderen beobachtbaren Klassen wie LiveData zu finden.
Die View wartet wie bei jedem anderen Ablauf auf StateFlow:
class LatestNewsActivity : ComponentActivity() { private val latestNewsViewModel: LatestNewsViewModel = TODO() // getViewModel() override fun onCreate(savedInstanceState: Bundle?) { // ... // Start a coroutine in the lifecycle scope lifecycleScope.launch { // repeatOnLifecycle launches the block in a new coroutine every time the // lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED. repeatOnLifecycle(Lifecycle.State.STARTED) { // Trigger the flow and start listening for values. // Note that this happens when lifecycle is STARTED and stops // collecting when the lifecycle is STOPPED latestNewsViewModel.uiState.collect { uiState -> // New value received when (uiState) { is LatestNewsUiState.Success -> showFavoriteNews(uiState.news) is LatestNewsUiState.Error -> showError(uiState.exception) } } } } } }
Wenn Sie einen beliebigen Ablauf in ein StateFlow konvertieren möchten, verwenden Sie den Zwischenoperator stateIn.
StateFlow, Flow und LiveData
StateFlow und LiveData haben Ähnlichkeiten. Beide sind Klassen für beobachtbare Daten und folgen einem ähnlichen Muster, wenn sie in Ihrer App-Architektur verwendet werden.
StateFlow und LiveData verhalten sich jedoch unterschiedlich:
- Für
StateFlowmuss ein Anfangszustand an den Konstruktor übergeben werden, fürLiveDatanicht. - Bei
LiveData.observe()wird die Registrierung des Nutzers automatisch aufgehoben, wenn der View in den StatusSTOPPEDwechselt. Bei der Erfassung über einenStateFlow- oder einen anderen Ablauf wird die Erfassung nicht automatisch beendet. Um dasselbe Verhalten zu erzielen, müssen Sie den Flow aus einemLifecycle.repeatOnLifecycle-Block erfassen.
Kalte Flows mit shareIn heiß machen
StateFlow ist ein aktiver Ablauf. Er bleibt im Arbeitsspeicher, solange er erfasst wird oder solange andere Verweise darauf von einem Garbage Collection-Root aus vorhanden sind. Mit dem Operator shareIn können Sie kalte Flows in heiße Flows umwandeln.
Anhand des in Kotlin-Flows erstellten callbackFlow können Sie die aus Firestore abgerufenen Daten zwischen Collectors teilen, indem Sie shareIn verwenden. So muss nicht für jeden Collector ein neuer Flow erstellt werden.
Sie müssen Folgendes übergeben:
- Eine
CoroutineScope, die zum Freigeben des Ablaufs verwendet wird. Dieser Bereich sollte länger als jeder Consumer aktiv sein, damit der gemeinsame Ablauf so lange wie nötig aktiv bleibt. - Die Anzahl der Elemente, die für jeden neuen Collector wiedergegeben werden sollen.
- Die Richtlinie für das Startverhalten.
class NewsRemoteDataSource( private val externalScope: CoroutineScope ) { val latestNews: Flow<List<ArticleHeadline>> = flow { // ... }.shareIn( externalScope, replay = 1, started = SharingStarted.WhileSubscribed() ) }
In diesem Beispiel gibt der latestNews-Ablauf das zuletzt ausgegebene Element an einen neuen Collector aus und bleibt aktiv, solange externalScope aktiv ist und es aktive Collectors gibt. Die SharingStarted.WhileSubscribed()-Startrichtlinie hält den Upstream-Produzenten aktiv, solange es aktive Abonnenten gibt. Es sind auch andere Startrichtlinien verfügbar, z. B. SharingStarted.Eagerly, um den Producer sofort zu starten, oder SharingStarted.Lazily, um die Freigabe nach dem ersten Abonnenten zu starten und den Flow für immer aktiv zu halten.
SharedFlow
Die Funktion shareIn gibt einen SharedFlow zurück, einen Hot Flow, der Werte an alle Consumer ausgibt, die Daten daraus abrufen. Eine SharedFlow ist eine hochgradig konfigurierbare Verallgemeinerung von StateFlow.
Sie können ein SharedFlow erstellen, ohne shareIn zu verwenden. Sie können beispielsweise einen SharedFlow verwenden, um Ticks an den Rest der App zu senden, damit alle Inhalte regelmäßig gleichzeitig aktualisiert werden. Neben dem Abrufen der neuesten Nachrichten möchten Sie vielleicht auch den Bereich mit Nutzerinformationen mit der Sammlung der Lieblingsthemen aktualisieren. Im folgenden Code-Snippet macht ein TickHandler ein SharedFlow verfügbar, damit andere Klassen wissen, wann sie ihre Inhalte aktualisieren müssen. Wie bei StateFlow verwenden Sie eine Sicherungsproperty vom Typ MutableSharedFlow in einer Klasse, um Elemente an den Flow zu senden:
// Class that centralizes when the content of the app needs to be refreshed class TickHandler( private val externalScope: CoroutineScope, private val tickIntervalMs: Long = 5000 ) { // Backing property to avoid flow emissions from other classes private val _tickFlow = MutableSharedFlow<Unit>(replay = 0) val tickFlow: SharedFlow<Unit> = _tickFlow init { externalScope.launch { while (true) { _tickFlow.emit(Unit) delay(tickIntervalMs) } } } }
class NewsRepository( // ... private val tickHandler: TickHandler, private val externalScope: CoroutineScope ) { init { externalScope.launch { // Listen for tick updates tickHandler.tickFlow.collect { refreshLatestNews() } } } suspend fun refreshLatestNews() { /* ... */ } // ... }
Sie haben folgende Möglichkeiten, das Verhalten von SharedFlow anzupassen:
- Mit
replaykönnen Sie eine Reihe von zuvor gesendeten Werten für neue Abonnenten noch einmal senden. - Mit
onBufferOverflowkönnen Sie eine Richtlinie dafür angeben, wann der Puffer mit zu sendenden Elementen gefüllt ist. Der Standardwert istBufferOverflow.SUSPEND. Das bedeutet, dass der Anrufer aufgelegt wird. Weitere Optionen sindDROP_LATESToderDROP_OLDEST.
MutableSharedFlow hat auch eine subscriptionCount-Property, die die Anzahl der aktiven Collectors enthält, damit Sie Ihre Geschäftslogik entsprechend optimieren können. MutableSharedFlow enthält auch die Funktion resetReplayCache, wenn Sie die letzten an den Flow gesendeten Informationen nicht noch einmal wiedergeben möchten.
Zusätzliche Ressourcen für den Ablauf
- Kotlin-Flows unter Android
- Kotlin-Flows unter Android testen
- Wichtige Informationen zu den Operatoren „shareIn“ und „stateIn“ von Flow
- Von LiveData zu Kotlin Flow migrieren
- Zusätzliche Ressourcen für Kotlin-Coroutinen und -Flow