FlowFlow e SharedFlow

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)
                    }
                }
            }
        }
    }
}
di Gemini Advanced.

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, mentre LiveData no.
  • LiveData.observe() annulla automaticamente la registrazione del consumatore quando vista va allo stato STOPPED, mentre la raccolta da un StateFlow o la raccolta di altri flussi non si interrompe automaticamente. Per ottenere lo stesso devi raccogliere il flusso da un Lifecycle.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 sono DROP_LATEST o DROP_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