Mogę obserwować LiveData z wielu fragmentów. Czy mogę to zrobić za pomocą Flow? Jeśli tak to jak?
Tak. Możesz to zrobić za pomocą emiti collect. Myśl emitjest podobna do danych na żywo postValuei collectjest podobna do observe. Podajmy przykład.
Magazyn
// 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()
}
Fragment
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
}
}
}
Możemy mieć wiele LiveData z jednego LiveData za pomocą map i switchMap. Czy jest jakiś sposób na posiadanie wielu Flowów z jednego źródła Flow?
Przepływ jest bardzo przydatny. Możesz po prostu stworzyć przepływ wewnątrz przepływu. Powiedzmy, że chcesz dołączyć znak stopnia do każdego z danych prognozy pogody.
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)
}
}
}
Następnie zbierz dane w Fragmentie takim samym jak nr 1. Oto, co się dzieje, gdy model widoku zbiera dane z repozytorium, a fragment zbiera dane z modelu widoku.
Korzystanie z MutableLiveData Mogę aktualizować dane z dowolnego miejsca za pomocą odwołania do zmiennej. Czy jest jakiś sposób, aby zrobić to samo z Flow?
Nie możesz emitować wartości poza przepływem. Blok kodu wewnątrz przepływu jest wykonywany tylko wtedy, gdy istnieje kolektor. Ale możesz przekonwertować przepływ na dane na żywo za pomocą rozszerzenia asLiveData z LiveData.
ViewModel
fun getWeatherForecast(): LiveData<String> {
return forecastRepository
.getWeatherForecastEveryTwoSeconds()
.asLiveData() // Convert flow to live data
}
W twoim przypadku możesz to zrobić
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
}
Edytować
Dzięki @mark za komentarz. Utworzenie nowego przepływu w modelu widoku dla getWeatherForecastfunkcji jest w rzeczywistości niepotrzebne. Można go ponownie zapisać jako
fun getWeatherForecast(): Flow<String> {
return forecastRepository
.getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
.map {
it + " °C"
}
}