Kotlin Flow vs Android LiveData

20

Tenho algumas perguntas sobre o Kotlin Flow

  1. Eu posso observar o LiveData de vários fragmentos. Posso fazer isso com o Flow? Se sim, então como?
  2. Podemos ter vários LiveData de um único LiveData usando map& switchMap. Existe alguma maneira de ter vários fluxos a partir de um único fluxo de origem?
  3. Usando MutableLiveDataEu posso atualizar dados de qualquer lugar usando a referência variável. Existe alguma maneira de fazer o mesmo com o Flow?

Eu tenho um caso de uso como: observarei um SharedPreferencesuso callbackFlow{...}que me dará uma única fonte de fluxo. A partir desse fluxo, desejo criar vários fluxos para cada par de valores-chave.

Isso pode parecer perguntas tolas. Eu sou novo no mundo Rx e Flow.

zoha131
fonte
Qual abordagem você adotou - Flow ou LiveData ?
IgorGanapolsky
2
Atualmente, estou usando o LiveData para visualizações e o Flow para todos os outros. No ViewModel, recebo Flow e emite LiveData para observar a partir de fragmentos.
zoha131
@ zoha131 você faz da maneira certa! Como o LiveData pode ser observado apenas no thread principal, eles se encaixam perfeitamente nas interações View <-> ViewModel. Depois, o Flows permite que você faça operações mais complexas no restante de sua arquitetura.
smora 15/03

Respostas:

15

Eu posso observar o LiveData de vários fragmentos. Posso fazer isso com o Flow? Se sim, então como?

Sim. Você pode fazer isso com emite collect. Pense emité semelhante aos dados ativos postValuee collecté semelhante a observe. Vamos dar um exemplo.

Repositório

// 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 ter vários LiveData de um único LiveData usando map & switchMap. Existe alguma maneira de ter vários fluxos de um único fluxo de origem?

O fluxo é muito útil. Você pode apenas criar fluxo dentro do fluxo. Digamos que você queira anexar um sinal de grau a cada um dos dados da previsão do 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) 
                }
    }
}

Em seguida, colete os dados no fragmento igual ao nº 1. Aqui o que acontece é que o modelo de exibição está coletando dados do repositório e o fragmento está coletando dados do modelo de exibição.

Usando MutableLiveData, eu posso atualizar dados de qualquer lugar usando a referência variável. Existe alguma maneira de fazer o mesmo com o Flow?

Você não pode emitir valor fora do fluxo. O bloco de código dentro do fluxo é executado apenas quando existe um coletor. Mas você pode converter o fluxo em dados ativos usando a extensão asLiveData do LiveData.

ViewModel

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

No seu caso, você pode fazer isso

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

Obrigado a @mark pelo seu comentário. Criar um novo fluxo no modelo de visualização para a getWeatherForecastfunção é realmente desnecessário. Pode ser reescrito como

fun getWeatherForecast(): Flow<String> {
        return forecastRepository
                .getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
                    .map {
                        it + " °C"
                    }
    }
Fatih
fonte
Não sei por que, mas tive a suposição de que não posso chamar a função collect () em vários locais para um único Flow. obrigado pela resposta.
precisa saber é o seguinte
11
Não. Você pode coletar o mesmo fluxo em vários locais. val sharedPref = getSharedPref()e você pode usar a coleta em vários lugares sharedPref.collect {}. A única coisa é que, como a coleta é suspensa, é necessário chamá-lo do bloco de corotina. E feliz em ajudar np :)
Fatih
para minha terceira pergunta, a solução alternativa pode ser um canal de transmissão.
precisa saber é o seguinte
Você pode verificar essa confirmação usando canais em vez de dados ao vivo. github.com/android/plaid/pull/770/commits/…
Fatih
11
Sim você está certo. É aqui que entra o fluxo. Os canais têm muitas coisas com as quais você precisa cuidar e são quentes, o que significa que estão sempre abertos, mesmo que não haja observadores. Mas, com o fluxo, você pode obter os mesmos benefícios sem preocupações, porque são frios. Assim em vez de canal eu acho que é melhor para o fluxo de uso
Fatih
3

Há uma nova Flow.asLiveData()função de extensão nos novos androidx.lifecyclepacotes ktx. Você pode aprender mais no meu artigo: https://www.netguru.com/codestories/android-coroutines-%EF%B8%8Fin-2020

Samuel Urbanowicz
fonte
Quando precisamos usar isso?
IgorGanapolsky 18/02
11
Quando você deseja satisfazer uma API que requer LiveData com uma instância de Flow
Samuel Urbanowicz
Segundo o google, temos que escolher LiveData ou Flow: codelabs.developers.google.com/codelabs/…
IgorGanapolsky
1

Em uma arquitetura de três camadas: apresentação do domínio de dados, o Flow deve ocorrer na camada de dados (bancos de dados, rede, cache ...) e depois como Samuel Urbanowicz mencionou, é possível mapear o Flow para o LiveData.

Em geral, o Flow é quase o que o Observable (ou Flowable) é para o RxJava. Não confunda com LiveData.

mais aqui: https://medium.com/@elizarov/cold-flows-hot-channels-d74769805f9

gts13
fonte
Para operações únicas (isto é, leitura de banco de dados), o LiveData é suficiente.
IgorGanapolsky 18/02