StateFlow
und SharedFlow
sind Flow APIs, mit denen Abläufe optimal Zustandsaktualisierungen und Werte an mehrere Nutzer ausgeben können.
StateFlow
StateFlow
ist ein beobachtbarer Fluss für Zustandsinhaber, der aktuelle und neue Statusaktualisierungen an seine Collectors sendet. Der Wert des aktuellen Status kann auch über die Property value
gelesen werden. Weisen Sie dem Attribut value
der Klasse MutableStateFlow
einen neuen Wert zu, um den Status zu aktualisieren und an den Ablauf zu senden.
In Android eignet sich StateFlow
hervorragend für Klassen, die einen beobachtbaren änderbaren Status beibehalten müssen.
Gemäß den Beispielen aus Kotlin-Abläufen kann ein StateFlow
aus dem LatestNewsViewModel
verfügbar gemacht werden, damit die View
auf Aktualisierungen der UI-Zustände warten und den Bildschirmstatus auch nach Konfigurationsänderungen beibehalten kann.
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 View 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 Ersteller. Alle Klassen, die aus dem StateFlow
erfassen, sind die Nutzer. Im Gegensatz zu einem kalten Ablauf, der mit dem flow
-Builder erstellt wurde, ist ein StateFlow
heiß: Das Erfassen aus dem Ablauf löst keinen Producer-Code aus. Ein StateFlow
ist immer aktiv und im Arbeitsspeicher und kommt nur dann für die automatische Speicherbereinigung infrage, wenn es von einem Stamm der automatischen Speicherbereinigung keine anderen Verweise darauf gibt.
Wenn ein neuer Nutzer mit der Erfassung aus dem Ablauf beginnt, erhält er den letzten Status im Stream und alle nachfolgenden Zustände. Dieses Verhalten finden Sie auch in anderen beobachtbaren Klassen wie LiveData
.
Der View
wartet wie jeder andere Ablauf auf StateFlow
:
class LatestNewsActivity : AppCompatActivity() {
private val latestNewsViewModel = // 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)
}
}
}
}
}
}
Mit dem Zwischenoperator stateIn
können Sie einen beliebigen Ablauf in einen StateFlow
konvertieren.
StateFlow, Flow und LiveData
StateFlow
und LiveData
haben Ähnlichkeiten. Beide sind beobachtbare Dateninhaberklassen und folgen bei der Verwendung in Ihrer Anwendungsarchitektur einem ähnlichen Muster.
Beachten Sie jedoch, dass sich StateFlow
und LiveData
unterschiedlich verhalten:
- Bei
StateFlow
muss ein Anfangszustand an den Konstruktor übergeben werden. BeiLiveData
ist dies nicht der Fall. LiveData.observe()
hebt die Registrierung des Nutzers automatisch auf, wenn die Ansicht in den StatusSTOPPED
wechselt. Die Erfassung aus einemStateFlow
oder einem anderen Ablauf wird hingegen nicht automatisch beendet. Um dasselbe Verhalten zu erzielen, müssen Sie den Ablauf aus einemLifecycle.repeatOnLifecycle
-Block erfassen.
Kaltfließen mit shareIn
heiß
StateFlow
ist ein heißer Ablauf. Er bleibt im Arbeitsspeicher, solange der Datenfluss erfasst wird oder während andere Verweise darauf in einem Stamm der automatischen Speicherbereinigung vorhanden sind. Mit dem Operator shareIn
können Sie Kaltflüsse heiß stellen.
Wenn Sie das in Kotlin-Abläufe erstellte callbackFlow
als Beispiel verwenden, können Sie die aus Firestore abgerufenen Daten mithilfe von shareIn
für andere Collectors freigeben, anstatt jeden Collector einen neuen Ablauf erstellen zu lassen.
Sie müssen Folgendes übergeben:
- Ein
CoroutineScope
, der zum Teilen des Ablaufs verwendet wird. Dieser Bereich sollte länger gelten als jeder Nutzer, damit der gemeinsame Ablauf so lange wie nötig aktiv bleibt. - Die Anzahl der Elemente, die an jeden neuen Collector noch einmal abgespielt werden sollen.
- Die Startverhaltensrichtlinie.
class NewsRemoteDataSource(...,
private val externalScope: CoroutineScope,
) {
val latestNews: Flow<List<ArticleHeadline>> = flow {
...
}.shareIn(
externalScope,
replay = 1,
started = SharingStarted.WhileSubscribed()
)
}
In diesem Beispiel gibt der Ablauf latestNews
das letzte ausgegebene Element an einen neuen Collector wieder und bleibt aktiv, solange externalScope
aktiv ist und aktive Collectors vorhanden sind. Die Startrichtlinie SharingStarted.WhileSubscribed()
hält den Upstream-Ersteller aktiv, solange aktive Abonnenten vorhanden sind. Es sind weitere Startrichtlinien verfügbar, z. B. SharingStarted.Eagerly
, um den Producer sofort zu starten, oder SharingStarted.Lazily
, um die Freigabe zu starten, nachdem der erste Abonnent angezeigt wurde, und den Ablauf dauerhaft aktiv hält.
SharedFlow
Die Funktion shareIn
gibt ein SharedFlow
zurück. Das ist ein Hot Flow, der Werte an alle Nutzer ausgibt, die daraus Daten sammeln. Ein SharedFlow
ist eine hoch konfigurierbare Generalisierung von StateFlow
.
Sie können ein SharedFlow
erstellen, ohne shareIn
zu verwenden. Beispielsweise können Sie mit SharedFlow
Ticks an den Rest der App senden, sodass alle Inhalte regelmäßig gleichzeitig aktualisiert werden. Vielleicht möchten Sie nicht nur die neuesten Nachrichten abrufen, sondern auch den Abschnitt mit den Nutzerinformationen mit der Sammlung der Lieblingsthemen aktualisieren. Im folgenden Code-Snippet stellt TickHandler
eine SharedFlow
zur Verfügung, damit andere Klassen wissen, wann ihr Inhalt aktualisiert werden muss. Verwenden Sie wie bei StateFlow
ein unterstützendes Attribut des Typs MutableSharedFlow
in einer Klasse, um Elemente an den Ablauf 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<Event<String>> = _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 können das Verhalten von SharedFlow
auf folgende Arten anpassen:
- Mit
replay
können Sie eine Reihe zuvor ausgegebener Werte für neue Abonnenten noch einmal senden. - Mit
onBufferOverflow
können Sie eine Richtlinie für den Fall angeben, dass der Zwischenspeicher voller zu sendender Elemente ist. Der Standardwert istBufferOverflow.SUSPEND
, wodurch der Aufrufer angehalten wird. Weitere Optionen sindDROP_LATEST
oderDROP_OLDEST
.
MutableSharedFlow
hat auch ein subscriptionCount
-Attribut, das die Anzahl der aktiven Collectors enthält, damit Sie Ihre Geschäftslogik entsprechend optimieren können. MutableSharedFlow
enthält auch eine resetReplayCache
-Funktion, wenn Sie die neuesten an den Ablauf gesendeten Informationen nicht noch einmal wiedergeben möchten.
Zusätzliche Ressourcen zum Ablauf
- Kotlin-Abläufe unter Android
- Kotlin-Abläufe auf Android-Geräten testen
- Wissenswertes zu den „shareIn“- und „stateIn“-Operatoren von Flow
- Migration von LiveData zu Kotlin-Ablauf
- Zusätzliche Ressourcen für Kotlin-Koroutinen und -Ablauf