StateFlow
e SharedFlow
sono API Flow
che consentono ai flussi di emettere in modo ottimale aggiornamenti di stato e valori su più
i consumatori.
StateFlow
StateFlow
è un flusso osservabile per gli stati che emette lo stato attuale e quello nuovo
ai suoi raccoglitori. Il valore dello stato corrente può essere letto anche tramite
value
proprietà. Per aggiornare lo stato e inviarlo al flusso, assegna un nuovo valore a
la proprietà value
del
MutableStateFlow
.
In Android, StateFlow
è ideale per le classi che devono mantenere
uno stato mutabile osservabile.
Seguendo gli esempi dei flussi Kotlin, viene eseguita una StateFlow
può essere esposto da LatestNewsViewModel
in modo che View
possa
rimanere in ascolto degli aggiornamenti sullo stato dell'interfaccia utente
e mantenere intrinsecamente lo stato della schermata
modifiche alla configurazione.
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()
}
Il corso responsabile dell'aggiornamento di un MutableStateFlow
è il producer,
e tutte le classi che raccolgono dal StateFlow
sono consumatori. Non mi piace
un flusso cold creato utilizzando il builder flow
, un StateFlow
è hot:
la raccolta dal flusso non attiva alcun codice producer. StateFlow
è sempre attivo e in memoria e diventa idoneo per la garbage
raccolta solo quando non esistono altri riferimenti da una garbage
la radice di raccolta.
Quando un nuovo consumatore inizia a raccogliere dati dal flusso, riceve l'ultima
nel flusso e in tutti gli stati successivi. Puoi scoprire che questo comportamento
in altre classi osservabili come
LiveData
View
ascolta StateFlow
come con qualsiasi altro flusso:
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)
}
}
}
}
}
}
Per convertire qualsiasi flusso in un StateFlow
, utilizza la classe
stateIn
operatore intermedio.
StateFlow, Flow e LiveData
StateFlow
e LiveData
hanno
le analogie. Entrambe sono classi di titolari dei dati osservabili ed entrambe seguono
un pattern simile se utilizzato nell'architettura dell'app.
Tuttavia, tieni presente che StateFlow
e
Le LiveData
si comportano in modo diverso:
StateFlow
richiede il passaggio di uno stato iniziale al costruttore, mentreLiveData
no.LiveData.observe()
annulla automaticamente la registrazione del consumatore quando vista va allo statoSTOPPED
, mentre la raccolta da unStateFlow
o la raccolta di altri flussi non si interrompe automaticamente. Per ottenere lo stesso devi raccogliere il flusso da unLifecycle.repeatOnLifecycle
bloccare.
Riscaldamento dei flussi freddi utilizzando shareIn
StateFlow
è un flusso hot: rimane in memoria finché il flusso è
o mentre esistono altri riferimenti alla garbage collection
principale. Puoi riscaldare i flussi freddi utilizzando la
shareIn
operatore.
L'utilizzo dell'oggetto callbackFlow
creato nei flussi Kotlin come
ad esempio, invece di chiedere a ogni raccoglitore di creare un nuovo flusso, puoi
i dati recuperati da Firestore tra i raccoglitori utilizzando shareIn
.
Devi superare quanto segue:
- Un
CoroutineScope
utilizzato per condividere il flusso. Questo ambito deve essere attivo di qualsiasi consumatore per mantenere attivo il flusso condiviso per tutto il tempo necessario. - Il numero di elementi da riprodurre per ogni nuovo raccoglitore.
- Il criterio relativo al comportamento iniziale.
class NewsRemoteDataSource(...,
private val externalScope: CoroutineScope,
) {
val latestNews: Flow<List<ArticleHeadline>> = flow {
...
}.shareIn(
externalScope,
replay = 1,
started = SharingStarted.WhileSubscribed()
)
}
In questo esempio, il flusso latestNews
ripete l'ultimo elemento emesso
a un nuovo raccoglitore e rimane attivo finché externalScope
è
attivi e ci sono raccoglitori attivi. SharingStarted.WhileSubscribed()
il criterio di avvio mantiene attivo il producer upstream mentre è attivo
abbonati. Sono disponibili altri criteri iniziali, ad esempio
SharingStarted.Eagerly
per avviare il producer immediatamente oppure
SharingStarted.Lazily
per iniziare a condividere quando appare il primo iscritto
e mantenere il flusso attivo per sempre.
SharedFlow
La funzione shareIn
restituisce un SharedFlow
, un flusso a caldo che emette valori
a tutti i consumatori che ne raccolgono i contenuti. Un SharedFlow
è un
generalizzazione altamente configurabile di StateFlow
.
Puoi creare un SharedFlow
senza utilizzare shareIn
. Ad esempio,
potresti utilizzare SharedFlow
per inviare segni di graduazione al resto dell'app in modo che
tutti i contenuti si aggiornano periodicamente
contemporaneamente. A parte
il recupero delle ultime notizie, potresti anche voler aggiornare
informazioni con la sua raccolta di argomenti preferiti. Nel seguente
snippet di codice, un TickHandler
espone un SharedFlow
in modo che altri
sappiano quando aggiornare i contenuti. Come per StateFlow
, utilizza un
una proprietà di supporto di tipo MutableSharedFlow
in una classe per inviare elementi
al flusso:
// 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() { ... }
...
}
Puoi personalizzare il comportamento di SharedFlow
nei seguenti modi:
replay
ti consente di inviare di nuovo un numero di valori emessi in precedenza per i nuovi abbonati.onBufferOverflow
consente di specificare un criterio per quando il buffer è piena di elementi da inviare. Il valore predefinito èBufferOverflow.SUSPEND
, che fa sospendere il chiamante. Le altre opzioni sonoDROP_LATEST
oDROP_OLDEST
.
MutableSharedFlow
ha anche una proprietà subscriptionCount
che contiene
il numero di raccoglitori attivi in modo che tu possa ottimizzare la tua attività
la logica di conseguenza. MutableSharedFlow
contiene anche un resetReplayCache
se non vuoi ripetere le ultime informazioni inviate al flusso.
Risorse di flusso aggiuntive
- Kotlin funziona su Android
- Testare i flussi Kotlin su Android
- Informazioni importanti sugli operatori shareIn e stateIn di Flow
- Migrazione da LiveData al flusso Kotlin
- Risorse aggiuntive per coroutine e flussi di Kotlin