Puedo observar LiveData desde múltiples Fragmentos. ¿Puedo hacer esto con Flow? Si es así, ¿entonces cómo?
Si. Puedes hacer esto con emit
y collect
. Think emit
es similar a los datos en vivo postValue
y collect
es 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 getWeatherForecast
función es realmente innecesario. Podría reescribirse como
fun getWeatherForecast(): Flow<String> {
return forecastRepository
.getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
.map {
it + " °C"
}
}