Kotlin Flow ve Android LiveData


20

Kotlin Flow hakkında bazı sorularım var

  1. LiveData'yı birden fazla parçadan gözlemleyebilirim. Flow ile yapabilir miyim? Cevabınız evet ise nasıl?
  2. map& Kullanarak tek bir LiveData öğesinden birden fazla LiveData alabiliriz switchMap. Tek bir Kaynak Akışından birden fazla Akış almanın herhangi bir yolu var mı?
  3. Kullanılması MutableLiveDatayerde değişken referans kullanılarak veri güncelleme yapabilirsiniz. Flow ile aynı şeyi yapmanın bir yolu var mı?

Ben böyle bir kullanım-durum var: Bir gözlemleyeceksiniz SharedPreferenceskullanarak callbackFlow{...}hangi bana tek kaynak Akış verecektir. Bu Akıştan, her bir anahtar / değer çifti için birden çok Akış oluşturmak istiyorum.

Bunlar aptalca sorular gibi gelebilir. Rx ve Flow dünyasında yeniyim.


Flow veya LiveData ile hangi yaklaşımı belirlediniz ?
IgorGanapolsky

2
Şu anda, görünümler için LiveData ve diğerleri için her şey için Flow kullanıyorum. ViewModel'de Flow alıyorum ve parçalardan gözlemlemek için LiveData yayar.
zoha131

@ zoha131 doğru şekilde yaparsınız! LiveData yalnızca ana iş parçacığında gözlemlenebildiğinden, View <-> ViewModel etkileşimlerine mükemmel uyum sağlar. Ardından Akışlar, mimarinizin geri kalanında daha karmaşık işlemler yapmanızı sağlar.
smora

Yanıtlar:


15

LiveData'yı birden fazla parçadan gözlemleyebilirim. Flow ile yapabilir miyim? Cevabınız evet ise nasıl?

Evet. Bunu emitve ile yapabilirsiniz collect. Düşünmek emitcanlı verilere benzer postValueve collectbenzerdir observe. Bir örnek verelim.

depo

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

fragman

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

Map & switchMap kullanarak tek bir LiveData öğesinden birden fazla LiveData alabiliriz. Tek bir Kaynak Akışından birden fazla Akış almanın herhangi bir yolu var mı?

Akış çok kullanışlıdır. Sadece akış içinde akış oluşturabilirsiniz. Hava tahmini verilerinin her birine derece işareti eklemek istediğinizi varsayalım.

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

Ardından Fragman'daki verileri # 1 ile aynı şekilde toplayın. Burada olan, görünüm modelinin depodan veri toplaması ve fragman, görünüm modelinden veri toplamasıdır.

MutableLiveData kullanarak değişken başvurusunu kullanarak verileri her yerden güncelleyebilirim. Flow ile aynı şeyi yapmanın bir yolu var mı?

Akış dışında değer veremezsiniz. Akış içindeki kod bloğu yalnızca herhangi bir toplayıcı olduğunda yürütülür. Ancak LiveData'dan asLiveData uzantısını kullanarak akışı canlı verilere dönüştürebilirsiniz.

ViewModel

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

Sizin durumunuzda bunu yapabilirsiniz

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
}

Düzenle

@Mark'a yaptığı yorum için teşekkürler. Görünüm modelinde getWeatherForecastişlev için yeni bir akış oluşturmak aslında gereksizdir. Olarak yeniden yazılabilir

fun getWeatherForecast(): Flow<String> {
        return forecastRepository
                .getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
                    .map {
                        it + " °C"
                    }
    }

Nedenini bilmiyorum ama tek bir Akış için birden fazla yerde collect () işlevini çağıramadığım varsayımım vardı. Cevap için teşekkürler.
zoha131

1
Hayır. Aynı akışı birden fazla yerde toplayabilirsiniz. val sharedPref = getSharedPref()ve toplama'yı birden fazla yerde kullanabilirsiniz sharedPref.collect {}. Tek şey, koleksiyonun askıya alınması olduğu için, onu coroutine bloğundan çağırmanız gerekir. Ve mutlu np yardım :)
Fatih

üçüncü sorum için geçici çözüm bir yayın kanalı olabilir.
zoha131

Canlı yayın yerine kanalları kullanmak için bu taahhüdü kontrol edebilirsiniz. github.com/android/plaid/pull/770/commits/…
Fatih

1
Evet haklısın. Burada akış devreye giriyor. Kanallarda dikkat etmeniz gereken çok şey var ve bunlar sıcak, yani gözlemci olmasa bile her zaman açık oldukları anlamına geliyor. Ancak akışla, soğuk olduğu için endişe duymadan aynı faydaları elde edebilirsiniz. Kanal yerine akışı kullanmanın daha iyi olduğunu düşünüyorum
Fatih

3

Flow.asLiveData()Yeni androidx.lifecyclektx paketlerinde yeni bir uzantı işlevi vardır . Makalemde daha fazla bilgi edinebilirsiniz: https://www.netguru.com/codestories/android-coroutines-%EF%B8%8Fin-2020


Bunu ne zaman kullanmalıyız?
IgorGanapolsky

1
Flow örneğiyle LiveData gerektiren bir API'yi karşılamak istediğinizde
Samuel Urbanowicz

Google göre, gerek seçim ya LiveData veya Akışı: codelabs.developers.google.com/codelabs/...
IgorGanapolsky

1

3 katmanlı bir mimaride: veri-etki alanı-sunumu, Akış veri katmanında (veritabanları, ağ, önbellek ...) yer almalı ve daha sonra Samuel Urbanowicz'in belirttiği gibi Flow'u LiveData ile eşleştirebilirsiniz.

Genel olarak Akış, RxJava için Neredeyse Gözlenebilir (veya Akıcı) olan şeydir. LiveData ile karıştırmayın.

buradan daha fazlası: https://medium.com/@elizarov/cold-flows-hot-channels-d74769805f9


Tek seferlik işlemler için (yani Veritabanı okuma), LiveData yeterlidir.
IgorGanapolsky
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.