Kotlin Flow vs Android LiveData


20

Ho alcune domande su Kotlin Flow

  1. Posso osservare LiveData da più frammenti. Posso farlo con Flow? Se sì allora come?
  2. Possiamo avere più LiveData da un singolo LiveData usando map& switchMap. Esiste un modo per avere più flussi da un singolo flusso sorgente?
  3. Utilizzando MutableLiveDataposso aggiornare i dati da qualsiasi luogo utilizzando il riferimento variabile. Esiste un modo per fare lo stesso con Flow?

Ho un caso d'uso del tipo: osserverò un SharedPreferencesutilizzo callbackFlow{...}che mi darà un flusso unico. Da quel flusso, voglio creare più flussi per ogni coppia chiave-valore.

Potrebbero sembrare domande stupide. Sono nuovo nel mondo Rx e Flow.


Su quale approccio hai scelto - Flow o LiveData ?
IgorGanapolsky,

2
Attualmente sto usando LiveData per le visualizzazioni e Flow per tutto il resto. In ViewModel, ricevo Flow ed emette LiveData per l'osservazione da frammenti.
zoha131,

@ zoha131 lo fai nel modo giusto! Poiché LiveData può essere osservato solo sul thread principale, si adattano perfettamente alle interazioni View <-> ViewModel. Quindi Flows ti consente di eseguire operazioni più complesse nel resto della tua architettura.
smora

Risposte:


15

Posso osservare LiveData da più frammenti. Posso farlo con Flow? Se sì allora come?

Sì. Puoi farlo con emite collect. Pensa emitè simile ai dati live postValueed collectè simile a observe. Facciamo un esempio.

deposito

// I just faked the weather forecast
val weatherForecast = listOf("10", "12", "9")

// This function returns flow of forecast data
// Whenever the data is fetched, it is emitted so that
// collector can collect (if there is any)
fun getWeatherForecastEveryTwoSeconds(): Flow<String> = flow { 
    for (i in weatherForecast) {
        delay(2000)
        emit(i)
    }
}

ViewModel

fun getWeatherForecast(): Flow<String> {
    return forecastRepository.getWeatherForecastEveryTwoSeconds()
}

Frammento

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    // Collect is suspend function. So you have to call it from a 
    // coroutine scope. You can create a new coroutine or just use 
    // lifecycleScope
    // https://developer.android.com/topic/libraries/architecture/coroutines
    lifecycleScope.launch {
            viewModel.getWeatherForecastEveryTwoSeconds().collect {
                    // Use the weather forecast data
                    // This will be called 3 times since we have 3 
                    // weather forecast data
            }
    }
}

Possiamo avere più LiveData da un singolo LiveData usando map & switchMap. Esiste un modo per avere più flussi da un singolo flusso sorgente?

Flow è molto utile. Puoi semplicemente creare un flusso all'interno del flusso. Diciamo che si desidera aggiungere il segno di laurea a ciascuno dei dati delle previsioni del tempo.

ViewModel

fun getWeatherForecast(): Flow<String> {
    return flow {
        forecastRepository
            .getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
                .map {
                    it + " °C"
                }
                .collect {
                    // This will send "10 °C", "12 °C" and "9 °C" respectively
                    emit(it) 
                }
    }
}

Quindi raccogliere i dati nel frammento come il n. 1. Qui ciò che accade è che il modello di visualizzazione sta raccogliendo dati dal repository e il frammento sta raccogliendo dati dal modello di visualizzazione.

Usando MutableLiveData posso aggiornare i dati da qualsiasi luogo usando il riferimento variabile. Esiste un modo per fare lo stesso con Flow?

Non è possibile emettere valore al di fuori del flusso. Il blocco di codice all'interno del flusso viene eseguito solo quando è presente un raccoglitore. Ma puoi convertire il flusso in dati live utilizzando l'estensione asLiveData da LiveData.

ViewModel

fun getWeatherForecast(): LiveData<String> {
    return forecastRepository
    .getWeatherForecastEveryTwoSeconds()
    .asLiveData() // Convert flow to live data
}

Nel tuo caso puoi farlo

private fun getSharedPrefFlow() = callbackFlow {
    val sharedPref = context?.getSharedPreferences("SHARED_PREF_NAME", MODE_PRIVATE)
    sharedPref?.all?.forEach {
        offer(it)
    }
}

getSharedPrefFlow().collect {
    val key = it.key
    val value = it.value
}

modificare

Grazie a @mark per il suo commento. La creazione di un nuovo flusso nel modello di visualizzazione per la getWeatherForecastfunzione non è effettivamente necessaria. Potrebbe essere riscritto come

fun getWeatherForecast(): Flow<String> {
        return forecastRepository
                .getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
                    .map {
                        it + " °C"
                    }
    }

Non so perché, ma avevo il presupposto che non posso chiamare la funzione collect () in più punti per un singolo flusso. Grazie per la risposta.
zoha131

1
No. Puoi raccogliere lo stesso flusso in più punti. val sharedPref = getSharedPref()e puoi utilizzare la raccolta in più punti sharedPref.collect {}. L'unica cosa è perché raccogliere è sospeso, è necessario chiamarlo dal blocco coroutine. E felice di aiutare np :)
Fatih

per la mia terza domanda, la soluzione potrebbe essere un canale di trasmissione.
zoha131

È possibile verificare questo commit per l'utilizzo dei canali anziché dei dati live. github.com/android/plaid/pull/770/commits/…
Fatih

1
Si hai ragione. È qui che entra in gioco il flusso. I canali hanno molte cose di cui devi occuparti e sono caldi, nel senso che sono sempre aperti anche se non ci sono osservatori. Ma con il flusso puoi ottenere gli stessi benefici senza preoccupazioni perché sono freddi. Quindi, invece del canale, penso che sia meglio usare il flusso
Fatih il

3

C'è una nuova Flow.asLiveData()funzione di estensione nei nuovi androidx.lifecyclepacchetti ktx. Puoi saperne di più nel mio articolo: https://www.netguru.com/codestories/android-coroutines-%EF%B8%8Fin-2020


Quando dobbiamo usare questo?
IgorGanapolsky,

1
Quando si desidera soddisfare un'API che richiede LiveData con un'istanza Flow
Samuel Urbanowicz,

Secondo Google, dobbiamo scegliere LiveData o Flow: codelabs.developers.google.com/codelabs/…
IgorGanapolsky

1

In un'architettura a 3 livelli: presentazione dominio-dati, Flow dovrebbe avvenire nel livello dati (database, rete, cache ...) e quindi, come indicato da Samuel Urbanowicz, è possibile mappare Flow a LiveData.

In generale, Flow è quasi ciò che l'Osservabile (o Flowable) è per RxJava. Non confonderlo con LiveData.

altro qui: https://medium.com/@elizarov/cold-flows-hot-channels-d74769805f9


Per le operazioni one-shot (ovvero lettura del database), LiveData è sufficiente.
IgorGanapolsky,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.