Kotlin Flow กับ Android LiveData


20

ฉันมีคำถามบางอย่างเกี่ยวกับ Kotlin Flow

  1. ฉันสามารถสังเกต LiveData ได้จากหลายแฟรกเมนต์ ฉันสามารถทำสิ่งนี้กับ Flow ได้หรือไม่ ถ้าใช่แล้วได้อย่างไร
  2. เราสามารถมี LiveData หลายรายการจาก LiveData เดียวโดยใช้&map switchMapมีวิธีใดที่จะมีการไหลหลายครั้งจากการไหลของแหล่งเดียว?
  3. ใช้MutableLiveDataฉันสามารถปรับปรุงข้อมูลได้จากทุกที่โดยใช้การอ้างอิงตัวแปร มีวิธีใดที่จะทำเช่นเดียวกันกับ Flow

ฉันมีกรณีใช้: ฉันจะสังเกตการSharedPreferencesใช้callbackFlow{...}ซึ่งจะให้ฉันไหลที่มาเดียว จาก Flow นั้นฉันต้องการสร้าง Flow หลายรายการสำหรับคู่คีย์ - ค่าแต่ละคู่

คำถามเหล่านี้อาจฟังดูไร้สาระ ฉันใหม่สำหรับ Rx และ Flow โลก


ซึ่งวิธีการที่คุณไม่ตั้งอยู่บน - ไหลหรือLiveData ?
IgorGanapolsky

2
ขณะนี้ฉันกำลังใช้ LiveData สำหรับการดูและการไหลสำหรับทุกสิ่งอื่น ๆ ใน ViewModel ฉันได้รับ Flow และปล่อย LiveData เพื่อสังเกตจากชิ้นส่วน
zoha131

@ zoha131 คุณทำถูกต้องแล้ว! เนื่องจาก LiveData สามารถสังเกตได้บนเธรดหลักเท่านั้นจึงจะสามารถดูการโต้ตอบของ ViewModel <-> จากนั้นโฟลว์ให้คุณทำการดำเนินการที่ซับซ้อนมากขึ้นในส่วนที่เหลือของสถาปัตยกรรมของคุณ
smora

คำตอบ:


15

ฉันสามารถสังเกต 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("SHARED_PREF_NAME", MODE_PRIVATE)
    sharedPref?.all?.forEach {
        offer(it)
    }
}

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

แก้ไข

ขอบคุณ @mark สำหรับความคิดเห็นของเขา การสร้างโฟลว์ใหม่ในโมเดลมุมมองสำหรับgetWeatherForecastฟังก์ชั่นนั้นไม่จำเป็นจริงๆ มันอาจจะเขียนใหม่เป็น

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

ฉันไม่รู้ว่าทำไม แต่ฉันมีข้อสันนิษฐานว่าฉันไม่สามารถเรียกฟังก์ชัน collect () ในหลาย ๆ ที่สำหรับ Flow เดียว ขอบคุณสำหรับคำตอบ.
zoha131

1
ไม่คุณสามารถรวบรวมโฟลว์เดียวกันในหลาย ๆ ที่ได้ และคุณสามารถใช้เก็บในสถานที่ต่างๆval sharedPref = getSharedPref() sharedPref.collect {}สิ่งเดียวที่เป็นเพราะการสะสมถูกระงับคุณต้องโทรหามันจากบล็อก coroutine และยินดีที่จะช่วย np :)
Fatih

สำหรับคำถามที่สามการแก้ปัญหาอาจเป็นช่องออกอากาศ
zoha131

คุณสามารถตรวจสอบการกระทำนี้สำหรับการใช้ช่องแทนข้อมูลสด github.com/android/plaid/pull/770/commits/…
Fatih

1
ใช่คุณถูก. นี่คือที่ไหลเข้ามาแชนแนลมีหลายสิ่งหลายอย่างที่คุณต้องดูแลและพวกมันมีความหมายร้อนที่พวกเขาเปิดอยู่เสมอแม้ว่าจะไม่มีผู้สังเกตการณ์ก็ตาม แต่ด้วยการไหลคุณจะได้รับประโยชน์แบบเดียวกันโดยไม่ต้องกังวลเพราะมันเย็น ดังนั้นแทนที่จะเป็นช่องทางฉันคิดว่ามันเป็นการดีที่จะใช้ flow
Fatih

3

มีFlow.asLiveData()ฟังก์ชั่นเสริมใหม่ในandroidx.lifecycleแพ็คเกจ ktx ใหม่ คุณสามารถเรียนรู้เพิ่มเติมในบทความของฉัน: https://www.netguru.com/codestories/android-coroutines-%EF%B8%8Fin-2020


เมื่อใดที่เราต้องใช้สิ่งนี้
IgorGanapolsky

1
เมื่อคุณต้องการตอบสนอง API ที่ต้องใช้ LiveData พร้อมอินสแตนซ์ Flow
Samuel Urbanowicz

ตาม google เราต้องเลือก LiveData หรือ Flow: codelabs.developers.google.com/codelabs//
IgorGanapolsky

1

ในสถาปัตยกรรม 3 ชั้น: การนำเสนอข้อมูลโดเมนการไหลควรเกิดขึ้นในชั้นข้อมูล (ฐานข้อมูลเครือข่ายแคช ... ) จากนั้นซามูเอล Urbanowiczกล่าวถึงคุณสามารถแมป Flow กับ LiveData ได้

โดยทั่วไปโฟลเป็นสิ่งที่สังเกตได้ (หรือ Flowable) สำหรับ RxJava อย่าสับสนกับ LiveData

เพิ่มเติมได้ที่นี่: https://medium.com/@elizarov/cold-flows-hot-channels-d74769805f9


สำหรับการใช้งานแบบครั้งเดียว (เช่นอ่านฐานข้อมูล) LiveData ก็เพียงพอ
IgorGanapolsky
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.