Kotlin Flow против Android LiveData - PullRequest
       63

Kotlin Flow против Android LiveData

2 голосов
/ 08 ноября 2019

У меня есть несколько вопросов о Kotlin Flow

  1. Я могу наблюдать LiveData из нескольких фрагментов. Могу ли я сделать это с Flow? Если да, то как?
  2. Мы можем иметь несколько LiveData из одной LiveData, используя map & switchMap. Есть ли способ получить несколько потоков из одного источника потока?
  3. Использование MutableLiveData Я могу обновить данные из любого места, используя ссылку на переменную. Есть ли способ сделать то же самое с Flow?

У меня есть такой пример использования: я буду наблюдать SharedPreferences с использованием callbackFlow{...}, который даст мне единственный источник Flow. Из этого потока я хочу создать несколько потоков для каждой пары ключ-значение.

Это может показаться глупым вопросом. Я новичок в мире Rx и Flow.

1 Ответ

1 голос
/ 09 ноября 2019

Я могу наблюдать LiveData из нескольких фрагментов. Могу ли я сделать это с Flow? Если да, то как?

Да. Вы можете сделать это с emit и collect. Подумайте, emit похоже на живые данные postValue, а collect похоже на observe. Давайте приведем пример.

Репозиторий

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

Фрагмент

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

Мы можем иметь несколько LiveData из одной LiveData, используя map & switchMap. Есть ли способ иметь несколько потоков из одного источника потока?

Поток очень удобно. Вы можете просто создать поток внутри потока. Допустим, вы хотите добавить знак градуса к каждому из данных прогноза погоды.

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

Затем собрать данные во Fragment так же, как # 1. Здесь происходит то, что модель представления собирает данные из хранилища, а фрагмент собирает данные из модели представления.

Используя MutableLiveData, я могу обновлять данные из любого места, используя ссылку на переменную. Есть ли способ сделать то же самое с Flow?

Вы не можете передать значение за пределы потока. Блок кода внутри потока выполняется только при наличии какого-либо коллектора. Но вы можете преобразовать поток в живые данные, используя расширение asLiveData из LiveData.

ViewModel

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

В вашем случае вы можете сделать это

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

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