LiveData

Lifecycle-aware component

LiveData ๋ž€

LiveData๋Š” ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐ ํ™€๋” ํด๋ž˜์Šค์ด๋‹ค. ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ์ผ๋ฐ˜ ํด๋ž˜์Šค์™€ ๋‹ฌ๋ฆฌ LiveData๋Š” ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ์ธ์‹ํ•œ๋‹ค. ์•กํ‹ฐ๋น„ํ‹ฐ, ํ”„๋ž˜๊ทธ๋จผํŠธ, ์„œ๋น„์Šค ๋“ฑ ์•ฑ ๊ตฌ์„ฑ์š”์†Œ์˜ ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ๊ณ ๋ คํ•œ๋‹ค. ์ˆ˜๋ช… ์ฃผ๊ธฐ ์ธ์‹์„ ํ†ตํ•ด LiveData๋Š” ํ™œ๋™ ์ˆ˜๋ช… ์ฃผ๊ธฐ ์ƒํƒœ์— ์žˆ๋Š” ์•ฑ ๊ตฌ์„ฑ์š”์†Œ ๊ด€์ฐฐ์ž๋งŒ ์—…๋ฐ์ดํŠธ ํ•œ๋‹ค.

Observer ํด๋ž˜์Šค๋กœ ํ‘œํ˜„๋˜๋Š” ๊ด€์ฐฐ์ž์˜ ์ˆ˜๋ช… ์ฃผ๊ธฐ๊ฐ€ STARTED | RESUME ์ƒํƒœ์ด๋ฉด LiveData๋Š” ๊ด€์ฐฐ์ž๋ฅผ ํ™œ์„ฑ์ƒํƒœ๋กœ ๊ฐ„์ฃผํ•œ๋‹ค. LiveData๋Š” ๊ด€์ฐฐ์ž์—๊ฒŒ๋งŒ ์—…๋ฐ์ดํŠธ ์ •๋ณด๋ฅผ ์•Œ๋ฆฐ๋‹ค. LifecycleOwner ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฐ์ฒด์™€ ํŽ˜์–ด๋ง๋œ ๊ด€์ฐฐ์ž๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ด€์ฐฐ์ž์— ๋Œ€์‘๋˜๋Š” Lifecycle ๊ฐ์ฒด์˜ ์ƒํƒœ๊ฐ€ DESTROYED ๋กœ ๋ณ€๊ฒฝ๋  ๋•Œ ๊ด€์ฐฐ์ž๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋‹ค. LiveData ๊ฐ์ฒด๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ๋‚˜ ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ ํŠนํžˆ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋ฉฐ, ๊ฐ์ฒด๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€์ฐฐํ•  ์ˆ˜ ์žˆ๊ณ  ์ˆ˜๋ช…์ฃผ๊ธฐ๊ฐ€ ๋๋‚˜๋Š” ์ฆ‰์‹œ ์ˆ˜์‹ ๊ฑฐ๋ถ€๊ฐ€ ๋˜์–ด๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๊ฑฑ์ • ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. LiveData ์‚ฌ์šฉ์— ๋Œ€ํ•œ ์ด์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

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

  • ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์—†์Œ ๊ด€์ฐฐ์ž๋Š” Lifecycle ๊ฐ์ฒด์— ๊ฒฐํ•ฉ๋˜์–ด ์žˆ์œผ๋ฉฐ ์—ฐ๊ฒฐ๋œ ์ˆ˜๋ช…์ฃผ๊ธฐ๊ฐ€ ๋๋‚˜๋ฉด ์ž๋™์œผ๋กœ ์‚ญ์ œ๋œ๋‹ค

  • ์ค‘์ง€๋œ ํ™œ๋™์œผ๋กœ ์ธํ•œ ๋น„์ •์ƒ ์ข…๋ฃŒ ์—†์Œ ์œ„์—์„œ ์„ค๋ช…ํ–ˆ๋“ฏ์ด LiveData ๋Š” STARTED | RESUME ์ƒํƒœ์ผ ๋•Œ ํ™œ์„ฑ์ƒํƒœ๋กœ ๊ฐ„์ฃผํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ๋ฐฑ์Šคํ…์— ์žˆ์„ ๋•Œ๋‚˜ ๊ด€์ฐฐ์ž๊ฐ€ ์ˆ˜๋ช… ์ฃผ๊ธฐ๊ฐ€ ๋น„ํ™œ์„ฑํ™” ์ƒํƒœ์ด๋ฉด ์–ด๋– ํ•œ LiveData ๋ฅผ ์ˆ˜์‹ ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

  • ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ๋” ์ด์ƒ ์ˆ˜๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์Œ UI ๊ตฌ์„ฑ์š”์†Œ๋Š” ๊ด€๋ จ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€์ฐฐ๋งŒ ํ•  ๋ฟ ๊ด€์ฐฐ์„ ์ค‘์ง€ํ•˜๊ฑฐ๋‚˜ ๋‹ค์‹œ ์‹œ์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค. LiveData๋Š” ๊ด€์ฐฐํ•˜๋Š” ๋™์•ˆ ๊ด€๋ จ ์ˆ˜๋ช… ์ฃผ๊ธฐ ์ƒํƒœ์˜ ๋ณ€๊ฒฝ์„ ์ธ์‹ํ•˜๋ฏ€๋กœ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌ๊ฐ€ ๋œ๋‹ค.

  • ์ตœ์‹  ๋ฐ์ดํ„ฐ ์œ ์ง€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ (onPause | onStop ) ํฌ๊ทธ๋ผ์šด๋“œ๋กœ ์ „ํ™˜๋˜์—ˆ์„ ๋•Œ LiveData๋Š” ๋‹ค์‹œ ํ™œ์„ฑํ™” ์ƒํƒœ๊ฐ€ ๋˜๋ฉด์„œ ๊ด€์ฐฐ์ž๋Š” ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹  ๋ฐ›๊ฒŒ ๋œ๋‹ค.

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

LiveData ์‚ฌ์šฉ

LiveData๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ViewModel ํด๋ž˜์Šค ์‚ฌ์šฉ๋œ๋‹ค. ์•ˆ๋“œ๋กœ์ด๋“œ MVVM ๋””์ž์ธ ํŒจํ„ด์—์„œ ์‚ฌ์šฉ๋œ๋‹ค.

  1. ViewModel ์— LiveData ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

class MyViewModel: ViewModel() {
    // ๊ด€์ฐฐํ•˜๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ๋‚˜ ํ”„๋ž˜๊ทธ๋จผํŠธ ๋“ฑ์—์„œ LiveData ๋ฅผ ๊ด€์ฐฐ๋งŒ ํ•˜๊ณ  ๋ฐ์ดํ„ฐ์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๊ฒŒ
    // ๊ฐ’์„ ์ฃผ์ž…ํ•˜๋Š” _resData ๋Š” ๋ณ€๊ฒฝ๊ฐ€๋Šฅ ํ•œ Mutableํ˜•์„ ์‚ฌ์šฉํ•˜๊ณ 
    // resData๋Š” ๊ฐ’์ด ๋ถˆ๊ฐ€๋Šฅํ•œ Immutableํ˜•์„ ์‚ฌ์šฉํ•œ๋‹ค.
    
    private val _resData: MutableLiveData<Int> = MutableLiveData()
    val resData
        get() = _resData
}

2. Observer ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ ๋‹ค. onChange()๋ฉ”์„œ๋“œ๋Š” LiveData ๊ฐ์ฒด๊ฐ€ ๋ณด์œ ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ, ํ˜ธ์ถœ๋œ๋‹ค.

public interface Observer<T> {
    /**
     * Called when the data is changed.
     * @param t  The new data
     */
    void onChanged(T t);
}
class MyActivity: AppCompatActivity() {

    private val val myViewModel by lazy { ViewModelProvider(this)[MyViewModel::class.java] }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        
        // observe() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด LiveData ๊ฐ์ฒด์— Observer ๊ฐ์ฒด๋ฅผ ์—ฐ๊ฒฐํ•œ๋‹ค.
        // observe() ๋ฉ”์„œ๋“œ๋Š” LifecyclewOwner ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›๋Š”๋‹ค.
        myViewModel.resData.observe(this) { data ->
            println(data)    // initialize liveData
        }
    }
}

๊ด€์ฐฐ์ž ๋“ฑ๋ก (observe ๋ฉ”์„œ๋“œ) ์€ ์•กํ‹ฐ๋น„ํ‹ฐ ๊ธฐ์ค€ onCreate()๋ฉ”์„œ๋“œ์—์„œ ํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. ์‚ฌ์šฉ์ž ํ™œ๋™์— ๋”ฐ๋ผ onResume()์€ ์—ฌ๋Ÿฌ๋ฒˆ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์„œ๋“œ์ด๋ฏ€๋กœ ์ค‘๋ณต ํ˜ธ์ถœ์„ ํ•˜์ง€ ์•Š๋„๋ก ํ•ด์•ผํ•œ๋‹ค. ๋˜ํ•œ STARTED ์ƒํƒœ๊ฐ€ ๋˜์—ˆ์„ ๋•Œ, LiveData๋Š” ์ฆ‰์‹œ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•œ๋‹ค. ์ด๋Š” ์ด๋ฏธ ๊ด€์ฐฐ์ž๊ฐ€ ๋“ฑ๋ก๋˜์–ด์žˆ์„ ๋•Œ๋งŒ ๋ฐœ์ƒํ•˜๋ฏ€๋กœ ์ด์ „์— ์„ค์ •์„ ํ•ด๋†“์•„์•ผ ํ•œ๋‹ค.

LiveData ๊ฐ์ฒด ์—…๋ฐ์ดํŠธ

์•„๋ž˜ ์˜ˆ์ œ๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ ๋‚ด ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๋ทฐ๋ชจ๋ธ์— ์žˆ๋Š” MutableLiveData ๊ฐ์ฒด์— ๊ฐ’์„ ์ˆ˜์ •ํ•˜๊ณ , ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€์ฐฐํ•˜๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ์—์„œ UI๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค.

# MyActivity.class
    ...
    override onCreate(saveInstanceState: Bundle?) {
        ...
        
        findViewById<Button>(R.id.btn_ok).setOnClickListener {
            myViewModel.plusNum()
        }
        viewModel.resData.observe(this) {
            findViewById<TextView>(R.id.num_text_view).text(it)
        }
    }
# MyViewModel.class
    ...
    fun plusNum() {
        val num = _resData.value ?: 0
        // ๋ผ์ด๋ธŒ๋ฐ์ดํ„ฐ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฉ”์„œ๋“œ๋Š” setValue / postValue() ๊ฐ€ ์žˆ๋‹ค.
        _resData.postValue(++num)
        // _resData.setValue(++num)
    }

setValue() : ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋Š” ๋ฉ”์ธ์Šค๋ ˆ๋“œ์—์„œ ํ˜ธ์ถœ๋œ๋‹ค. postValue() : ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋Š” ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋ฉ”์ธ์Šค๋ ˆ๋“œ์— ๊ฐ’์„ post ํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.

// todo ...

Last updated