[AAC] LiveData

dwjeongยท2023๋…„ 10์›” 26์ผ
0

์•ˆ๋“œ๋กœ์ด๋“œ

๋ชฉ๋ก ๋ณด๊ธฐ
6/28
post-custom-banner

๐Ÿ”Ž LiveData๋ž€

๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ (Observable) ๋ฐ์ดํ„ฐ ํ™€๋” ํด๋ž˜์Šค๋กœ, ๋‹ค๋ฅธ Observable ๋ฐ์ดํ„ฐ์™€ ๋‹ฌ๋ฆฌ ์•กํ‹ฐ๋น„ํ‹ฐ, ํ”„๋ž˜๊ทธ๋จผํŠธ ๋˜๋Š” ์„œ๋น„์Šค์™€ ๊ฐ™์€ ๋‹ค๋ฅธ ์•ฑ ์ปดํฌ๋„ŒํŠธ์˜ ๋ผ์ดํ”„์‚ฌ์ดํด์„ ๊ณ ๋ คํ•จ.
์ฆ‰, ํ™œ์„ฑ(active) ๋ผ์ดํ”„์‚ฌ์ดํด ์ƒํƒœ์— ์žˆ๋Š” ์•ฑ ์ปดํฌ๋„ŒํŠธ ๊ด€์ฐฐ์ž(Observer)๋งŒ ์—…๋ฐ์ดํŠธํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•จ.

  • LiveData๋Š” Observer ํด๋ž˜์Šค๋กœ ํ‘œํ˜„๋˜๋Š” ์˜ต์ €๋ฒ„๋งŒ ๊ณ ๋ คํ•˜๋ฉฐ, ํ•ด๋‹น ๋ผ์ดํ”„ ์‚ฌ์ดํด์ด STARTED ๋˜๋Š” RESUMED ์ƒํƒœ์— ์žˆ๋Š” ๊ฒฝ์šฐ๋ฅผ ํ™œ์„ฑ ์ƒํƒœ๋กœ ๊ฐ„์ฃผํ•˜์—ฌ ์—…๋ฐ์ดํŠธ์— ๋Œ€ํ•œ ์•Œ๋ฆผ์„ ์ œ๊ณตํ•จ.

  • LifecycleOwner์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฐœ์ฒด์™€ ๊ด€๋ จ๋œ ์˜ต์ €๋ฒ„๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Œ.
    ํ•ด๋‹น ๋ผ์ดํ”„์‚ฌ์ดํด ๊ฐ์ฒด์˜ ์ƒํƒœ๊ฐ€ DESTROYED๋กœ ๋ณ€๊ฒฝ๋˜๋ฉด ์˜ต์ €๋ฒ„๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Œ.
    -> ์•กํ‹ฐ๋น„ํ‹ฐ, ํ”„๋ž˜๊ทธ๋จผํŠธ์— ์œ ์šฉ. (๋ผ์ดํ”„์‚ฌ์ดํด์ด ์†Œ๋ฉธ๋˜๋ฉด ์ฆ‰์‹œ ๊ตฌ๋… ํ•ด์ง€ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ)




๐Ÿ“– LiveData ์‚ฌ์šฉ์˜ ์žฅ์ 


1. UI๊ฐ€ ๋ฐ์ดํ„ฐ ์ƒํƒœ์™€ ์ผ์น˜ํ•จ์„ ๋ณด์žฅ.
LiveData๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ Observer ๊ฐœ์ฒด์— ์•Œ๋ฆฌ๊ณ , ๋”ฐ๋ผ์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค UI๋ฅผ ์—…๋ฐ์ดํŠธํ•  ํ•„์š”๊ฐ€ ์—†์Œ.

2. ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€
์˜ต์ €๋ฒ„๋Š” ๋ผ์ดํ”„์‚ฌ์ดํด ๊ฐ์ฒด์— ๋ฐ”์ธ๋”ฉ๋˜๋ฉฐ ์—ฐ๊ฒฐ๋œ ๋ผ์ดํ”„์‚ฌ์ดํด์ด ์†Œ๋ฉธ๋  ๋•Œ ์Šค์Šค๋กœ ์ •๋ฆฌํ•จ.

3. ์ค‘์ง€๋œ ์•กํ‹ฐ๋น„ํ‹ฐ๋กœ ์ธํ•œ ์ถฉ๋Œ์ด ์—†์Œ.
์˜ต์ €๋ฒ„์˜ ๋ผ์ดํ”„์‚ฌ์ดํด์ด ๋น„ํ™œ์„ฑ ์ƒํƒœ์ธ ๊ฒฝ์šฐ (์˜ˆ: ๋ฐฑ์Šคํƒ์— ์žˆ๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ) LiveData ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•˜์ง€ ์•Š์Œ.

4. ์ˆ˜๋™ ๋ผ์ดํ”„์‚ฌ์ดํด ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š” ์—†์Œ.
UI ๊ตฌ์„ฑ ์š”์†Œ๋Š” ๊ด€๋ จ๋œ ๋ฐ์ดํ„ฐ๋งŒ ๊ด€์ฐฐํ•˜๋ฉฐ ๊ด€์ฐฐ์„ ์ค‘์ง€ํ•˜๊ฑฐ๋‚˜ ์žฌ๊ฐœํ•˜์ง€ ์•Š์Œ. LiveData๋Š” ๊ด€์ฐฐ ๋„์ค‘์— ๊ด€๋ จ ๋ผ์ดํ”„์‚ฌ์ดํด ์ƒํƒœ ๋ณ€๊ฒฝ์„ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•จ.

5. ํ•ญ์ƒ ์ตœ์‹  ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ.
๋ผ์ดํ”„์‚ฌ์ดํด์ด ๋น„ํ™œ์„ฑ์ธ ๊ฒฝ์šฐ ๋‹ค์‹œ ํ™œ์„ฑ ์ƒํƒœ๊ฐ€ ๋˜๋ฉด ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•จ.
(๋ฐฑ๊ทธ๋ผ์šด๋“œ์— ์žˆ๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ๋‹ค์‹œ ํ™”๋ฉด์œผ๋กœ ๋Œ์•„์˜ค๋ฉด ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ๋ฐ›์Œ.)

6. ์˜ฌ๋ฐ”๋ฅธ ๊ตฌ์„ฑ ๋ณ€๊ฒฝ
์•กํ‹ฐ๋น„ํ‹ฐ๋‚˜ ํ”„๋ž˜๊ทธ๋จผํŠธ๊ฐ€ ๋””๋ฐ”์ด์Šค ํšŒ์ „๊ณผ ๊ฐ™์€ ๊ตฌ์„ฑ ๋ณ€๊ฒฝ์œผ๋กœ ๋‹ค์‹œ ์ƒ์„ฑ๋˜๋ฉด ์ฆ‰์‹œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•จ.

7. ๋ฆฌ์†Œ์Šค ๊ณต์œ 
์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์—ฌ LiveData ๊ฐ์ฒด๋ฅผ ์‹œ์Šคํ…œ ์„œ๋น„์Šค์— ํ•œ๋ฒˆ ์—ฐ๊ฒฐํ•˜๊ณ , ํ•ด๋‹น ๋ฆฌ์†Œ์Šค๊ฐ€ ํ•„์š”ํ•œ ์˜ต์ €๋ฒ„๋Š” LiveData๊ฐ์ฒด๋ฅผ ๊ฐ์‹œํ•  ์ˆ˜ ์žˆ์Œ.




๐Ÿ“– LiveData ๊ฐ์ฒด๋กœ ์ž‘์—…ํ•˜๊ธฐ

  1. ํŠน์ • ์œ ํ˜•์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์œ ํ•  LiveData ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑ.
    ์ผ๋ฐ˜์ ์œผ๋กœ ViewModel ํด๋ž˜์Šค ๋‚ด์—์„œ ์ˆ˜ํ–‰๋จ.

  2. LiveData ๊ฐ์ฒด์˜ ๋ณด์œ  ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์ž‘์—…์„ ์ œ์–ดํ•˜๋Š” onChanged() ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•˜๋Š” Observer ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•จ. ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ ๋˜๋Š” ํ”„๋ž˜๊ทธ๋จผํŠธ์™€ ๊ฐ™์€ UI ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ƒ์„ฑํ•จ.

  3. observe() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Observer๊ฐ์ฒด๋ฅผ LiveData ๊ฐ์ฒด์— ๋“ฑ๋ก.
    observer() ๋ฉ”์„œ๋“œ๋Š” LifecycleOwner ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•จ.
    Observer ๊ฐ์ฒด๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์•กํ‹ฐ๋น„ํ‹ฐ๋‚˜ ํ”„๋ž˜๊ทธ๋จผํŠธ์™€ ๊ฐ™์€ UI ์ปจํŠธ๋กค๋Ÿฌ์—์„œ LiveData ๊ฐ์ฒด์— ๋“ฑ๋กํ•จ.


LiveData ๊ฐ์ฒด์— ์ €์žฅ๋œ ๊ฐ’์ด ์—…๋ฐ์ดํŠธ ๋˜๋ฉด, ์—ฐ๊ฒฐ๋œ LifecycleOwner๊ฐ€ ํ™œ์„ฑ ์ƒํƒœ์— ์žˆ๋Š” ํ•œ ๋ชจ๋“  ๋“ฑ๋ก๋œ ๊ด€์ฐฐ์ž๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•จ.

LiveData๋Š” UI ์ปจํŠธ๋กค๋Ÿฌ ์˜ต์ €๋ฒ„๊ฐ€ ์—…๋ฐ์ดํŠธ์— ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•จ.
LiveData ๊ฐ์ฒด๊ฐ€ ๋ณด์œ ํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด UI๊ฐ€ ์ž๋™์œผ๋กœ ์‘๋‹ตํ•˜์—ฌ ์—…๋ฐ์ดํŠธ๋จ.


๐Ÿ“ ์ฐธ๊ณ 
observeForever(Observer) ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—ฐ๊ฒฐ๋œ LifecycleOwner ๊ฐ์ฒด ์—†์ด ์˜ต์ €๋ฒ„๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Œ
-> ์ด ๊ฒฝ์šฐ ์˜ต์ €๋ฒ„๋Š” ํ•ญ์ƒ ํ™œ์„ฑ ์ƒํƒœ๋กœ ๊ฐ„์ฃผ๋˜๋ฏ€๋กœ ํ•ญ์ƒ ์ˆ˜์ • ์‚ฌํ•ญ์— ๋Œ€ํ•ด ์•Œ๋ฆผ์„ ๋ฐ›์Œ.
์ด๋Ÿฐ ์˜ต์ €๋ฒ„๋Š” removeObserver(Observer) ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Œ.



๐Ÿ“š LiveData ๊ฐ์ฒด ์ƒ์„ฑํ•˜๊ธฐ

LiveData๋Š” List์™€ ๊ฐ™์€ Collections๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฐœ์ฒด๋ฅผ ํฌํ•จํ•˜์—ฌ ๋ชจ๋“  ๋ฐ์ดํ„ฐ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ž˜ํผ. LiveData ๊ฐ์ฒด๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ViewModel ๊ฐ์ฒด ๋‚ด์— ์ €์žฅ๋˜๋ฉฐ getter๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์•ก์„ธ์Šค๋จ.

LiveData ๊ฐ์ฒด ๋‚ด์˜ ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ๋Š” ์„ค์ •๋˜์ง€ ์•Š์Œ.

class NameViewModel : ViewModel() {

    // Create a LiveData with a String
    val currentName: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }

    // Rest of the ViewModel...
}

UI ์—…๋ฐ์ดํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” LiveData ๊ฐ์ฒด๋ฅผ ์•กํ‹ฐ๋น„ํ‹ฐ๋‚˜ ํ”„๋ž˜๊ทธ๋จผํŠธ ๋Œ€์‹  ViewModel ๊ฐ์ฒด์— ์ €์žฅํ•ด์•ผํ•˜๋Š” ์ด์œ 

  • ๋ณต์žกํ•œ ์ฝ”๋“œ์˜ ์•กํ‹ฐ๋น„ํ‹ฐ์™€ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด. (To avoid bloated activiteds and fragments.) UI ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ์—ญํ• ์„ ํ•˜์ง€๋งŒ ๋ฐ์ดํ„ฐ ์ƒํƒœ๋ฅผ ๋ณด์œ ํ•˜์ง€ ์•Š์Œ.

  • LiveData ์ธ์Šคํ„ด์Šค๋ฅผ ํŠน์ • ์•กํ‹ฐ๋น„ํ‹ฐ๋‚˜ ํ”„๋ž˜๊ทธ๋จผํŠธ ์ธ์Šคํ„ด์Šค์™€ ๋ถ„๋ฆฌ์‹œํ‚ค๊ณ  LiveData ๊ฐ์ฒด๊ฐ€ ๊ตฌ์„ฑ ๋ณ€๊ฒฝ์œผ๋กœ๋ถ€ํ„ฐ ์‚ด์•„๋‚จ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด.

๐Ÿ“š LiveData ๊ฐ์ฒด ๊ด€์ฐฐํ•˜๊ธฐ

onCreate() ๋ฉ”์„œ๋“œ๊ฐ€ LiveData ๊ฐ์ฒด๋ฅผ ๊ด€์ฐฐํ•˜๊ธฐ์— ์ ์ ˆํ•œ ์ด์œ 

  • ์‹œ์Šคํ…œ์ด ์•กํ‹ฐ๋น„ํ‹ฐ๋‚˜ ํ”„๋ž˜๊ทธ๋จผํŠธ์˜ onResume()์—์„œ ์ค‘๋ณต ํ˜ธ์ถœ์„ ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด.

  • ์•กํ‹ฐ๋น„ํ‹ฐ ๋˜๋Š” ํ”„๋ž˜๊ทธ๋จผํŠธ๊ฐ€ ํ™œ์„ฑ ์ƒํƒœ๊ฐ€ ๋˜์ž๋งˆ์ž ํ‘œ์‹œํ•  ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด. ์•ฑ ์ปดํฌ๋„ŒํŠธ๊ฐ€ STARTED ์ƒํƒœ๊ฐ€ ๋˜๋ฉด ๊ด€์ฐฐ ์ค‘์ธ LiveData ๊ฐ์ฒด๋กœ๋ถ€ํ„ฐ ๊ฐ€์žฅ ์ตœ์‹  ๊ฐ’์„ ์ˆ˜์‹ ํ•จ.

LiveData๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ์—…๋ฐ์ดํŠธ๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ํ™œ์„ฑํ™”๋œ ์˜ต์ €๋ฒ„์—๊ฒŒ๋งŒ ์ „์†ก.

class NameActivity : AppCompatActivity() {

    // Use the 'by viewModels()' Kotlin property delegate
    // from the activity-ktx artifact
    private val model: NameViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Other code to setup the activity...

        // Create the observer which updates the UI.
        val nameObserver = Observer<String> { newName ->
            // Update the UI, in this case, a TextView.
            nameTextView.text = newName
        }

        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        model.currentName.observe(this, nameObserver)
    }
}

๐Ÿ“š LiveData ๊ฐ์ฒด ์—…๋ฐ์ดํŠธ

LiveData๋Š” ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•œ public ๋ฉ”์„œ๋“œ๊ฐ€ ์—†์Œ.
MutableLiveData ํด๋ž˜์Šค๋Š” setValue(T) ๋ฐ postValue(T) ๋ฉ”์„œ๋“œ๋ฅผ ๊ณต๊ฐœํ•˜๋ฉฐ
LiveData ๊ฐ์ฒด์— ์ €์žฅ๋œ ๊ฐ’์„ ์ˆ˜์ •ํ•ด์•ผ ํ•  ๊ฒฝ์šฐ ์ด ๋ฉ”์„œ๋“œ๋“ค์„ ์‚ฌ์šฉํ•ด์•ผ ํ•จ.

MutableLiveData๋Š” ViewModel์—์„œ ์‚ฌ์šฉ๋˜๊ณ , ViewModel์€ ์˜ต์ €๋ฒ„์—๊ฒŒ ๋ถˆ๋ณ€์˜ LiveData ๊ฐ์ฒด๋ฅผ ๋…ธ์ถœํ•จ.

button.setOnClickListener {
    val anotherName = "John Doe"
    model.currentName.setValue(anotherName)
}

๐Ÿ“š Room์œผ๋กœ LiveData ์‚ฌ์šฉ

Room ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” LiveData ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” observable ์ฟผ๋ฆฌ๋ฅผ ์ง€์›ํ•จ.
Observable ์ฟผ๋ฆฌ๋Š” DAO์˜ ์ผ๋ถ€๋กœ ์ž‘์„ฑ๋จ.

Room์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์—…๋ฐ์ดํŠธ๋  ๋•Œ LiveData ๊ฐ์ฒด๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑ.

์ƒ์„ฑ๋œ ์ฝ”๋“œ๋Š” ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์ฟผ๋ฆฌ๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰.

  • UI์— ํ‘œ์‹œ๋œ ๋ฐ์ดํ„ฐ์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋™๊ธฐํ™”ํ•˜๋Š”๋ฐ ์œ ์šฉ.


๐Ÿ“– ์•ฑ ์•„ํ‚คํ…์ฒ˜์—์„œ์˜ LiveData

  • LiveData์™€ UI ์ปจํŠธ๋กค๋Ÿฌ
    LiveData๋Š” ์—”ํ‹ฐํ‹ฐ(์•กํ‹ฐ๋น„ํ‹ฐ ๋ฐ ํ”„๋ž˜๊ทธ๋จผํŠธ)์˜ ๋ผ์ดํ”„์‚ฌ์ดํด์„ ๊ณ ๋ คํ•˜๋ฉฐ, ๋ผ์ดํ”„์‚ฌ์ดํด ์†Œ์œ ์ž์™€ ViewModel ๊ฐ์ฒด์™€ ๊ฐ™์€ ์ˆ˜๋ช…์ด ๋‹ค๋ฅธ ๊ฐ์ฒด ๊ฐ„์˜ ํ†ต์‹ ์— ์‚ฌ์šฉํ•จ.

ViewModel์˜ ์ฃผ์š” ์—ญํ• ์€ UI ๊ด€๋ จ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด๋ฏ€๋กœ LiveData ๊ฐ์ฒด๋ฅผ ๋ณด์œ ํ•˜๊ธฐ ์ข‹์Œ. ViewModel์—์„œ LiveData ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  UI ๋ ˆ์ด์–ด์— ๋…ธ์ถœ์‹œํ‚ด.

์•กํ‹ฐ๋น„ํ‹ฐ์™€ ํ”„๋ž˜๊ทธ๋จผํŠธ๋Š” LiveData ์ธ์Šคํ„ด์Šค๋ฅผ ๋ณด์œ ํ•ด์„œ๋Š” ์•ˆ๋จ.
-> UI ์ปจํŠธ๋กค๋Ÿฌ์˜ ์—ญํ• ์€ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ์ƒํƒœ๋ฅผ ๋ณด์œ ํ•˜๋Š” ๊ฒƒ.
-> ์•กํ‹ฐ๋น„ํ‹ฐ์™€ ํ”„๋ž˜๊ทธ๋จผํŠธ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์œ ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ๋” ์‰ฌ์›€.


  • LiveData์™€ ๋ฐ์ดํ„ฐ ๋ ˆ์ด์–ด ํด๋ž˜์Šค
    LiveData๋Š” ๋ฐ์ดํ„ฐ์˜ ๋น„๋™๊ธฐ ์ŠคํŠธ๋ฆผ์„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์„ค๊ณ„๋˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ Repository์—์„œ LiveData๋ฅผ ๋ณด์œ ํ•  ๊ฒฝ์šฐ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ฐจ๋‹จ๋  ์ˆ˜ ์žˆ์Œ.
//๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ฐจ๋‹จ๋  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ
class UserRepository {

    // DON'T DO THIS! LiveData objects should not live in the repository.
    fun getUsers(): LiveData<List<User>> {
        ...
    }

    fun getNewPremiumUsers(): LiveData<List<User>> {
        return getUsers().map { users ->
            // This is an expensive call being made on the main thread and may
            // cause noticeable jank in the UI!
            users
                .filter { user ->
                  user.isPremium
                }
          .filter { user ->
              val lastSyncedTime = dao.getLastSyncedTime()
              user.timeCreated > lastSyncedTime
                }
    }
}
  • ์•ฑ์˜ ๋‹ค๋ฅธ ๋ ˆ์ด์–ด์—์„œ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•ด์•ผํ•  ๊ฒฝ์šฐ Kotlin Flows๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ViewModel์—์„œ
    asLiveData()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ LiveData๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด์•ผ ํ•จ.

  • Java๋กœ ๋นŒ๋“œ๋œ ์ฝ”๋“œ๋ฒ ์ด์Šค๋Š” ์ฝœ๋ฐฑ ํ˜น์€ RxJava์™€ ๊ฒฐํ•ฉํ•˜์—ฌ Executors๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด์•ผ ํ•จ.



๐Ÿ“– LiveData ํ™•์žฅ

//์ฃผ์‹ ๊ฐ€๊ฒฉ ์—…๋ฐ์ดํŠธ 

class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
    private val stockManager = StockManager(symbol)

    private val listener = { price: BigDecimal ->
        value = price
    }

    override fun onActive() {
        stockManager.requestPriceUpdates(listener)
    }

    override fun onInactive() {
        stockManager.removeUpdates(listener)
    }
}
  1. onActive() ๋ฉ”์„œ๋“œ๋Š” LiveData ๊ฐ์ฒด์— ํ™œ์„ฑ ์˜ต์ €๋ฒ„๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ํ˜ธ์ถœ๋จ.
    ์ด ๋ฉ”์„œ๋“œ์—์„œ ์ฃผ์‹ ๊ฐ€๊ฒฉ ์—…๋ฐ์ดํŠธ๋ฅผ ๊ด€์ฐฐํ•˜๊ธฐ ์‹œ์ž‘ํ•ด์•ผํ•จ.

  2. onInactive() ๋ฉ”์„œ๋“œ๋Š” LiveData ๊ฐ์ฒด์— ํ™œ์„ฑ ์˜ต์ €๋ฒ„๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ํ˜ธ์ถœ๋จ.
    ํ™œ์„ฑ ์˜ต์ €๋ฒ„๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— StockManager ์„œ๋น„์Šค์™€ ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•  ์ด์œ ๊ฐ€ ์—†์Œ.

  3. setValue(T) ๋ฉ”์„œ๋“œ๋Š” LiveData ์ธ์Šคํ„ด์Šค์˜ ๊ฐ’์„ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ํ™œ์„ฑํ™” ๋œ ์˜ต์ €๋ฒ„์—๊ฒŒ ์•Œ๋ฆผ.


//์ด๋ ‡๊ฒŒ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
public class MyFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val myPriceListener: LiveData<BigDecimal> = ...
        myPriceListener.observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
            // Update the UI.
        })
    }
}





  • ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ตฌํ˜„
class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
    private val stockManager: StockManager = StockManager(symbol)

    private val listener = { price: BigDecimal ->
        value = price
    }

    override fun onActive() {
        stockManager.requestPriceUpdates(listener)
    }

    override fun onInactive() {
        stockManager.removeUpdates(listener)
    }

    companion object {
        private lateinit var sInstance: StockLiveData

        @MainThread
        fun get(symbol: String): StockLiveData {
            sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)
            return sInstance
        }
    }
}


class MyFragment : Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
            // Update the UI.
        })

    }

์—ฌ๋Ÿฌ ํ”„๋ž˜๊ทธ๋จผํŠธ์™€ ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ StockLiveData ์ธ์Šคํ„ด์Šค ๊ด€์ฐฐ ๊ฐ€๋Šฅ. (ํ”„๋ž˜๊ทธ๋จผํŠธ๋‚˜ ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ๋ณด์ด๊ณ  ์žˆ๊ณ  -visible, ํ™œ์„ฑ์ธ ๊ฒฝ์šฐ์—๋งŒ ์—ฐ๊ฒฐ.)



๐Ÿ“– LiveData ๋ณ€ํ™˜

LiveData ๊ฐ์ฒด์— ์ €์žฅ๋œ ๊ฐ’์„ ์˜ต์ €๋ฒ„์—๊ฒŒ ์ „๋‹ฌํ•˜๊ธฐ ์ „์— ํ•ด๋‹น ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜, ๋‹ค๋ฅธ LiveData ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ Transformation ํด๋ž˜์Šค๊ฐ€ ์ œ๊ณต๋จ.

  • Transformations.map()
    LiveData ๊ฐ์ฒด์— ์ €์žฅ๋œ ๊ฐ’์— ๋ณ€ํ™˜ ํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ downstream์œผ๋กœ ๋ณด๋ƒ„.
val userLiveData: LiveData<User> = UserLiveData()
val userName: LiveData<String> = userLiveData.map {
    user -> "${user.name} ${user.lastName}"
}

  • Transformations.switchMap()
    map()๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ LiveData ๊ฐ์ฒด์— ์ €์žฅ๋œ ๊ฐ’์— ํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ downstream ์œผ๋กœ ์ „ํŒŒ.
private fun getUser(id: String): LiveData<User> {
  ...
}
val userId: LiveData<String> = ...
val user = userId.switchMap { id -> getUser(id) }



+) 231209 ๋‚ด์šฉ ์ถ”๊ฐ€

LiveData<Y> switchMap (LiveData<X> trigger, 
                Function<X, LiveData<Y>> func)

trigger ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด func์— ์ ์šฉ๋จ. func์ด null์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ LiveData๋Š” backed๋˜์ง€ ์•Š์Œ.
func์€ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋จ.


 MutableLiveData userIdLiveData = ...;
 LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
     repository.getUserById(id));

 void setUserId(String userId) {
      this.userIdLiveData.setValue(userId);
 }
 

์œ„ ์ฝ”๋“œ์—์„œ repository๊ฐ€ User(1, "Jane"), User(2, "John") ์ด๋ผ๋Š” ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ–ˆ์„ ๋•Œ, userIdLiveData์— 1์ด ์„ค์ •๋˜๋ฉด switchMap์€ getUserById(1)์„ ํ˜ธ์ถœํ•˜์—ฌ LiveData๋ฅผ ๋ฐ˜ํ™˜ํ•จ.
๋”ฐ๋ผ์„œ userLiveData๋Š” User(1, "Jane")์„ ๋ฐฉ์ถœํ•จ.

userId=2๋กœ setUserId ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด userIdLiveData๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๊ณ  ์ €์žฅ์†Œ์—์„œ ID๊ฐ€ 2์ธ user๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•œ ์š”์ฒญ์ด ์ž๋™์œผ๋กœ ํŠธ๋ฆฌ๊ฑฐ๋จ.
userLiveData๋Š” User(2, "John")์„ ๋ฐฉ์ถœํ•จ.
repository.getUserById(1)์—์„œ ๋ฐ˜ํ™˜๋œ LiveData๋Š” ์†Œ์Šค์—์„œ ์ œ๊ฑฐ๋จ.



์˜ˆ์‹œ)
์ฃผ์†Œ๋ฅผ ์ž…๋ ฅ๋ฐ›์•„ ํ•ด๋‹น ์ฃผ์†Œ์˜ ์šฐํŽธ๋ฒˆํ˜ธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” UI ์š”์†Œ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ

  1. ์ฒซ๋ฒˆ์งธ ์ฝ”๋“œ
    UI์—์„œ getPostalCode()๋ฅผ ํ˜ธ์ถœํ•  ๋–„๋งˆ๋‹ค ์ด์ „ LiveData ๊ฐ์ฒด์—์„œ ๋“ฑ๋ก ํ•ด์ œํ•˜๊ณ  ์ƒˆ ์ธ์Šคํ„ด์Šค์— ๋“ฑ๋กํ•ด์•ผ ํ•จ.
class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() {

    private fun getPostalCode(address: String): LiveData<String> {
        // DON'T DO THIS
        return repository.getPostCode(address)
    }
}



  1. ๊ฐœ์„ ๋œ ์ฝ”๋“œ
    postalCode ํ•„๋“œ์™€ ๊ด€๋ จ๋œ ํ™œ์„ฑ ์˜ต์ €๋ฒ„๊ฐ€ ์žˆ๋Š” ํ•œ, addressInput์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ํ•„๋“œ์˜ ๊ฐ’์ด ๋‹ค์‹œ ๊ณ„์‚ฐ๋˜๊ณ  ๊ฒ€์ƒ‰๋จ.
    ๋”ฐ๋ผ์„œ ViewModel ๊ฐ์ฒด๋Š” ์‰ฝ๊ฒŒ LiveData์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ์–ป๊ณ  ๋ณ€ํ™˜ ๊ทœ์น™์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Œ.
class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() {
    private val addressInput = MutableLiveData<String>()
    val postalCode: LiveData<String> = addressInput.switchMap {
            address -> repository.getPostCode(address) }


    private fun setInput(address: String) {
        addressInput.value = address
    }
}


๐Ÿ“– ์—ฌ๋Ÿฌ ๊ฐœ์˜ LiveData ๋ณ‘ํ•ฉ

  • MediatorLiveData

์—ฌ๋Ÿฌ LiveData ์›๋ณธ์„ ๋ณ‘ํ•ฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” LiveData์˜ ํ•˜์œ„ ํด๋ž˜์Šค.
MediatorLiveData ๊ฐ์ฒด์˜ ๊ด€์ฐฐ์ž๋Š” LiveData ์›๋ณธ ๊ฐ์ฒด ์ค‘ ํ•˜๋‚˜๋ผ๋„ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ํŠธ๋ฆฌ๊ฑฐ๋จ.

์˜ˆ์‹œ)

UI์— ์žˆ๋Š” LiveData ๊ฐ์ฒด๊ฐ€ ๋กœ์ปฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋˜๋Š” ๋„คํŠธ์›Œํฌ์—์„œ ์—…๋ฐ์ดํŠธ๋  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ
๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ์™€ ๊ด€๋ จ๋œ LiveData ๊ฐ์ฒด.
๋„คํŠธ์›Œํฌ์—์„œ ์•ก์„ธ์Šค๋œ ๋ฐ์ดํ„ฐ์™€ ๊ด€๋ จ๋œ LiveData ๊ฐ์ฒด.
๋ฅผ MediatorLiveData ๊ฐ์ฒด์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Œ.

post-custom-banner

0๊ฐœ์˜ ๋Œ“๊ธ€