StateFlow
und SharedFlow
sind Flow APIs, die es Abläufen ermöglichen, Statusaktualisierungen optimal und Werte an mehrere Nutzer auszugeben.
StateFlow
StateFlow
ist ein observable-Stream mit Statushalter, der die aktuellen und neuen Statusaktualisierungen an seine Collector sendet. 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 Property value
der Klasse MutableStateFlow
einen neuen Wert zu.
Unter Android eignet sich StateFlow
hervorragend für Klassen, die einen beobachtbaren veränderbaren Zustand beibehalten müssen.
Gemäß den Beispielen aus Kotlin-Abläufen kann eine StateFlow
über die LatestNewsViewModel
freigegeben werden, damit die View
auf Aktualisierungen des UI-Status hören und den Bildschirmstatus bei 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 einer MutableStateFlow
verantwortlich ist, ist der Ersteller. Alle Klassen, die aus der StateFlow
erfassen, sind die Nutzer. Im Gegensatz zu einem kalten Ablauf, der mit dem flow
-Builder erstellt wurde, ist ein StateFlow
heiß: Die Erhebung aus dem Ablauf löst keinen Erstellercode aus. Ein StateFlow
ist immer aktiv und im Arbeitsspeicher. Es kommt nur dann für die Garbage Collection infrage, wenn es keine anderen Verweise von einem Garbage Collection-Stamm darauf gibt.
Wenn ein neuer Nutzer Daten aus dem Ablauf erfasst, empfängt er den letzten Status im Stream und alle nachfolgenden Status. Dieses Verhalten ist auch bei anderen beobachtbaren Klassen wie LiveData
zu finden.
Die View
überwacht wie bei jedem anderen Ablauf 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)
}
}
}
}
}
}
Wenn Sie einen beliebigen Ablauf in einen StateFlow
konvertieren möchten, verwenden Sie den Zwischenoperator stateIn
.
StateFlow, Flow und LiveData
StateFlow
und LiveData
haben Ähnlichkeiten. Beide sind beobachtbare Dateninhaberklassen und folgen bei Verwendung in Ihrer App-Architektur einem ähnlichen Muster.
StateFlow
und LiveData
verhalten sich jedoch unterschiedlich:
- Bei
StateFlow
muss dem Konstruktor ein Anfangsstatus übergeben werden, beiLiveData
ist das 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 stoppt dagegen nicht automatisch. Wenn Sie dasselbe Verhalten erzielen möchten, müssen Sie den Fluss aus einemLifecycle.repeatOnLifecycle
-Block erfassen.
Kaltströme mit shareIn
erwärmen
StateFlow
ist ein heißer Ablauf. Er bleibt im Arbeitsspeicher, solange der Ablauf erfasst wird oder es andere Verweise darauf von einem Garbage-Collection-Stamm gibt. Mit dem Operator shareIn
können Sie kalte Abläufe in heiße Abläufe umwandeln.
Anhand der in Kotlin-Abläufen erstellten callbackFlow
können Sie anstelle eines neuen Ablaufs für jeden Collector die aus Firestore abgerufenen Daten mithilfe von shareIn
für alle Collector freigeben.
Sie müssen Folgendes übergeben:
- Eine
CoroutineScope
, mit der der Ablauf freigegeben wird. Dieser Bereich sollte länger dauern als alle anderen Nutzer, um den gemeinsamen Ablauf so lange wie nötig am Laufen zu halten. - Die Anzahl der Elemente, die an jeden neuen Collector übertragen werden sollen.
- Die Richtlinie zum Startverhalten.
class NewsRemoteDataSource(...,
private val externalScope: CoroutineScope,
) {
val latestNews: Flow<List<ArticleHeadline>> = flow {
...
}.shareIn(
externalScope,
replay = 1,
started = SharingStarted.WhileSubscribed()
)
}
In diesem Beispiel spielt der latestNews
-Stream das letzte gesendete Element für einen neuen Collector noch einmal ab und bleibt aktiv, solange externalScope
aktiv ist und es aktive Collector gibt. Mit der SharingStarted.WhileSubscribed()
-Startrichtlinie bleibt der Upstream-Produzent aktiv, solange es aktive Abonnenten gibt. Es sind auch andere Startrichtlinien verfügbar, z. B. SharingStarted.Eagerly
, um den Stream sofort zu starten, oder SharingStarted.Lazily
, um die Freigabe nach dem ersten Abonnenten zu starten und den Stream dauerhaft aktiv zu halten.
SharedFlow
Die Funktion shareIn
gibt einen SharedFlow
zurück, einen Hot-Flow, der Werte an alle Abnehmer sendet, die ihn abrufen. Eine SharedFlow
ist eine hochgradig konfigurierbare Verallgemeinerung von StateFlow
.
Sie können ein SharedFlow
erstellen, ohne shareIn
zu verwenden. Sie können beispielsweise mit einem SharedFlow
-Element Tick-Signale an den Rest der App senden, damit alle Inhalte regelmäßig gleichzeitig aktualisiert werden. Neben den neuesten Nachrichten kannst du auch den Bereich mit den Nutzerinformationen mit der Sammlung der Lieblingsthemen aktualisieren. Im folgenden Code-Snippet stellt ein TickHandler
ein SharedFlow
bereit, damit andere Klassen wissen, wann der Inhalt aktualisiert werden muss. Verwenden Sie wie bei StateFlow
eine Back-End-Property vom Typ 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 haben folgende Möglichkeiten, das Verhalten von SharedFlow
anzupassen:
- Mit
replay
kannst du eine Reihe zuvor gesendeter Werte für neue Abonnenten noch einmal senden. - Mit
onBufferOverflow
können Sie eine Richtlinie für den Fall angeben, dass der Puffer voll mit zu sendenden Elementen ist. Der Standardwert istBufferOverflow.SUSPEND
, was dazu führt, dass der Anrufer angehalten wird. Weitere Optionen sindDROP_LATEST
oderDROP_OLDEST
.
MutableSharedFlow
hat auch das Attribut subscriptionCount
, 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.
Weitere Ressourcen zu Abläufen
- Kotlin-Abläufe unter Android
- Kotlin-Abläufe auf Android-Geräten testen
- Wichtige Informationen zu den shareIn- und stateIn-Operatoren von Flow
- Von LiveData zu Kotlin Flow migrieren
- Zusätzliche Ressourcen für Kotlin-Koroutinen und -Ablauf