Kotlin Flow vs Android LiveData

20

Saya punya beberapa pertanyaan tentang Kotlin Flow

  1. Saya dapat mengamati LiveData dari beberapa Fragmen. Bisakah saya melakukan ini dengan Flow? Jika ya lalu bagaimana?
  2. Kami dapat memiliki beberapa LiveData dari satu LiveData menggunakan map& switchMap. Apakah ada cara untuk memiliki beberapa aliran dari satu sumber sumber?
  3. Menggunakan MutableLiveDataSaya dapat memperbarui data dari mana saja menggunakan referensi variabel. Apakah ada cara untuk melakukan hal yang sama dengan Flow?

Saya memiliki use-case seperti: Saya akan mengamati SharedPreferencespenggunaan callbackFlow{...}yang akan memberi saya aliran sumber tunggal. Dari Arus itu, saya ingin membuat beberapa Arus untuk setiap pasangan nilai kunci.

Ini mungkin terdengar pertanyaan konyol. Saya baru di dunia Rx and Flow.

zoha131
sumber
Pendekatan mana yang Anda pilih - Flow atau LiveData ?
IgorGanapolsky
2
Saat ini, saya menggunakan LiveData untuk tampilan dan Aliran untuk semua yang lain. Di ViewModel, saya menerima Flow dan memancarkan LiveData untuk mengamati dari fragmen.
zoha131
@ zoha131 Anda melakukannya dengan cara yang benar! Karena LiveData dapat diamati hanya pada utas utama, mereka sangat cocok untuk melihat <-> interaksi ViewModel. Kemudian Arus memungkinkan Anda melakukan operasi yang lebih kompleks di seluruh arsitektur Anda.
smora

Jawaban:

15

Saya dapat mengamati LiveData dari beberapa Fragmen. Bisakah saya melakukan ini dengan Flow? Jika ya lalu bagaimana?

Iya. Anda dapat melakukan ini dengan emitdan collect. Berpikir emitmirip dengan data langsung postValuedan collectmirip dengan observe. Mari kita beri contoh.

Gudang

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

Pecahan

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

Kami dapat memiliki beberapa LiveData dari satu LiveData menggunakan peta & switchMap. Apakah ada cara untuk memiliki beberapa aliran dari satu sumber sumber?

Flow sangat berguna. Anda bisa membuat aliran di dalam aliran. Katakanlah Anda ingin menambahkan tanda derajat ke setiap data ramalan cuaca.

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

Kemudian kumpulkan data dalam Fragmen sama dengan # 1. Di sini yang terjadi adalah model tampilan mengumpulkan data dari repositori dan fragmen mengumpulkan data dari model tampilan.

Menggunakan MutableLiveData saya dapat memperbarui data dari mana saja menggunakan referensi variabel. Apakah ada cara untuk melakukan hal yang sama dengan Flow?

Anda tidak dapat memancarkan nilai di luar aliran. Blok kode di dalam aliran hanya dieksekusi ketika ada kolektor. Tetapi Anda dapat mengonversi aliran ke data langsung dengan menggunakan ekstensi asLiveData dari LiveData.

ViewModel

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

Dalam kasus Anda, Anda bisa melakukan ini

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
}

Edit

Terima kasih kepada @mark atas komentarnya. Membuat aliran baru dalam model tampilan untuk getWeatherForecastfungsi sebenarnya tidak perlu. Itu dapat ditulis ulang sebagai

fun getWeatherForecast(): Flow<String> {
        return forecastRepository
                .getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
                    .map {
                        it + " °C"
                    }
    }
Fatih
sumber
Saya tidak tahu mengapa, tetapi saya memiliki asumsi bahwa saya tidak dapat memanggil fungsi collect () di banyak tempat untuk satu Aliran tunggal. Terima kasih atas jawabannya.
zoha131
1
Tidak. Anda dapat mengumpulkan aliran yang sama di banyak tempat. val sharedPref = getSharedPref()dan Anda dapat menggunakan koleksi di banyak tempat sharedPref.collect {}. Satu-satunya hal adalah karena pengumpulan ditangguhkan, Anda harus memanggilnya dari blok coroutine. Dan dengan senang hati membantu np :)
Fatih
untuk pertanyaan ketiga saya, solusinya mungkin saluran siaran.
zoha131
Anda dapat memeriksa komit ini untuk menggunakan saluran alih-alih data langsung. github.com/android/plaid/pull/770/commits/…
Fatih
1
Ya kamu benar. Di sinilah mengalir masuk. Saluran memiliki banyak hal yang harus Anda urus dan panas artinya selalu terbuka meskipun tidak ada pengamat. Tetapi dengan aliran Anda bisa mendapatkan manfaat yang sama tanpa khawatir karena mereka dingin. Jadi, alih-alih saluran saya pikir lebih baik menggunakan aliran
Fatih
3

Ada Flow.asLiveData()fungsi ekstensi baru di androidx.lifecyclepaket ktx baru . Anda dapat mempelajari lebih lanjut di artikel saya: https://www.netguru.com/codestories/android-coroutines-%EF%B8%8Fin-2020

Samuel Urbanowicz
sumber
Kapan kita perlu menggunakan ini?
IgorGanapolsky
1
Saat Anda ingin memenuhi API yang membutuhkan LiveData dengan instance Flow
Samuel Urbanowicz
Menurut google, kita harus memilih LiveData atau Flow: codelabs.developers.google.com/codelabs/…
IgorGanapolsky
1

Dalam arsitektur 3-tier: data-domain-presentasi, Flow harus terjadi di lapisan data (database, jaringan, cache ...) dan kemudian seperti yang disebutkan Urban Urbanicicz Anda dapat memetakan Flow ke LiveData.

Secara umum, Flow hampir seperti apa yang Dapat Diamati (atau Flowable) untuk RxJava. Jangan bingung dengan LiveData.

lebih lanjut di sini: https://medium.com/@elizarov/cold-flows-hot-channels-d74769805f9

gts13
sumber
Untuk operasi satu-shot (mis. Database dibaca), LiveData sudah cukup.
IgorGanapolsky