Kotlin Flow vs Android LiveData

20

Tengo algunas preguntas sobre Kotlin Flow

  1. Puedo observar LiveData desde múltiples Fragmentos. ¿Puedo hacer esto con Flow? Si es así, ¿entonces cómo?
  2. Podemos tener múltiples LiveData desde un solo LiveData usando map& switchMap. ¿Hay alguna manera de tener múltiples flujos de una sola fuente de flujo?
  3. Utilizando MutableLiveDatapuedo actualizar datos desde cualquier lugar utilizando la referencia de variable. ¿Hay alguna manera de hacer lo mismo con Flow?

Tengo un caso de uso como: Observaré un SharedPreferencesuso callbackFlow{...}que me dará un flujo de fuente única. A partir de ese flujo, quiero crear múltiples flujos para cada par clave-valor.

Estas pueden sonar preguntas tontas. Soy nuevo en el mundo de Rx y Flow.

zoha131
fuente
¿En qué enfoque te decidiste : Flow o LiveData ?
IgorGanapolsky
2
Actualmente, estoy usando LiveData para vistas y Flow para todo lo demás. En ViewModel, recibo Flow y emite LiveData para observar desde fragmentos.
zoha131
@ zoha131 lo haces de la manera correcta! Como LiveData solo se puede observar en el subproceso principal, se ajustan perfectamente a las interacciones Ver <-> VerModelo. Luego, Flows le permite realizar operaciones más complejas en el resto de su arquitectura.
Smora

Respuestas:

15

Puedo observar LiveData desde múltiples Fragmentos. ¿Puedo hacer esto con Flow? Si es así, ¿entonces cómo?

Si. Puedes hacer esto con emity collect. Think emites similar a los datos en vivo postValuey collectes similar a observe. Vamos a dar un ejemplo.

Repositorio

// 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()
}

Fragmento

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
            }
    }
}

Podemos tener múltiples LiveData desde un solo LiveData usando map & switchMap. ¿Hay alguna manera de tener múltiples flujos de una sola fuente de flujo?

El flujo es muy útil. Simplemente puede crear flujo dentro del flujo. Digamos que desea agregar un signo de grado a cada uno de los datos de pronóstico del tiempo.

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) 
                }
    }
}

Luego, recopile los datos en Fragmento igual que # 1. Aquí lo que sucede es que el modelo de vista está recopilando datos del repositorio y el fragmento está recopilando datos del modelo de vista.

Usando MutableLiveData puedo actualizar datos desde cualquier lugar usando la referencia de variable. ¿Hay alguna manera de hacer lo mismo con Flow?

No puede emitir valor fuera del flujo. El bloque de código dentro del flujo solo se ejecuta cuando hay algún recopilador. Pero puede convertir el flujo en datos en vivo utilizando la extensión asLiveData de LiveData.

ViewModel

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

En tu caso puedes hacer esto

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
}

Editar

Gracias a @mark por su comentario. Crear un nuevo flujo en el modelo de vista para la getWeatherForecastfunción es realmente innecesario. Podría reescribirse como

fun getWeatherForecast(): Flow<String> {
        return forecastRepository
                .getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
                    .map {
                        it + " °C"
                    }
    }
Fatih
fuente
No sé por qué, pero suponía que no puedo llamar a la función collect () en varios lugares para un solo Flow. gracias por la respuesta.
zoha131
1
No. Puede recopilar el mismo flujo en varios lugares. val sharedPref = getSharedPref()y puedes usar recoger en varios lugares sharedPref.collect {}. Lo único es que recoger es suspender, debe llamarlo desde el bloque de rutina. Y feliz de ayudar np :)
Fatih
para mi tercera pregunta, la solución podría ser un canal de transmisión.
zoha131
Puede verificar esta confirmación para usar canales en lugar de datos en vivo. github.com/android/plaid/pull/770/commits/…
Fatih
1
Sí, tiene usted razón. Aquí es donde entra el flujo. Los canales tienen muchas cosas de las que tiene que ocuparse y están calientes, lo que significa que siempre están abiertos incluso si no hay observadores. Pero con el flujo puede obtener los mismos beneficios sin ninguna preocupación porque están fríos. Entonces, en lugar de canal, creo que es mejor usar flow
Fatih
3

Hay una nueva Flow.asLiveData()función de extensión en los nuevos androidx.lifecyclepaquetes ktx. Puede obtener más información en mi artículo: https://www.netguru.com/codestories/android-coroutines-%EF%B8%8Fin-2020

Samuel Urbanowicz
fuente
¿Cuándo necesitamos usar esto?
IgorGanapolsky
1
Cuando desea satisfacer una API que requiere LiveData con una instancia de Flow
Samuel Urbanowicz
Según Google, tenemos que elegir LiveData o Flow: codelabs.developers.google.com/codelabs/…
IgorGanapolsky
1

En una arquitectura de 3 niveles: presentación de dominio de datos, Flow debe tener lugar en la capa de datos (bases de datos, red, caché ...) y luego, como Samuel Urbanowicz mencionó, puede asignar Flow a LiveData.

En general, Flow es casi lo que el Observable (o Flowable) es para RxJava. No lo confundas con LiveData.

más aquí: https://medium.com/@elizarov/cold-flows-hot-channels-d74769805f9

gts13
fuente
Para operaciones de una sola vez (es decir, lectura de base de datos), LiveData es suficiente.
IgorGanapolsky