๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿš€ Development/Android

[์•ˆ๋“œ๋กœ์ด๋“œ] JetPack์˜ LiveData๋ž€?

by Jay Din 2023. 5. 22.
728x90
๋ฐ˜์‘ํ˜•

LiveData ๋ž€?

LiveData๋Š” Data์˜ ๋ณ€๊ฒฝ์„ ๊ด€์ฐฐํ•  ์ˆ˜์žˆ๋Š” Data Holder ํด๋ž˜์Šค์ด๋‹ค.

LiveData๋Š” ์•ˆ๋“œ๋กœ์ด๋“œ ์ƒ๋ช…์ฃผ๊ธฐ(LifeCycle)์„ ์•Œ๊ณ  ์žˆ๋‹ค.

์ฆ‰, Activity, Frgment, Service ๋“ฑ๊ณผ ๊ฐ™์€ ์•ˆ๋“œ๋กœ์ด๋“œ ์ปดํฌ๋„ŒํŠธ์˜ ์ƒ๋ช…์ฃผ๊ธฐ(Lifecycle)๋ฅผ ์ธ์‹ํ•˜๋ฉฐ ๊ทธ์— ๋”ฐ๋ผ LiveData๋Š” ํ™œ์„ฑ ์ƒํƒœ์ผ ๋•Œ๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•œ๋‹ค. ํ™œ์„ฑ ์ƒํƒœ๋ž€ Started ๋˜๋Š” Resumed๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ๋น„ํ™œ๋™ ์ƒํƒœ์ผ ๋•, LiveData๋Š” ๋ฐ์ดํ„ฐ์˜ ๋ณ€ํ™”๋ฅผ ์•Œ๋ฆฌ์ง€ ์•Š๋‹ค. Destoryed ๋œ ์ƒํƒœ์˜ lifecycle ์—์„œ๋Š” LiveData ๊ฐ์ฒด๋ฅผ Observeํ•˜์ง€ ์•Š๋Š”๋‹ค.

LiveData ๊ฐ์ฒด๋Š” Observer ๊ฐ์ฒด์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋œ๋‹ค. LiveData๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋ฐ์ดํ„ฐ์— ๋ณ€ํ™”๊ฐ€ ์ผ์–ด๋‚  ๊ฒฝ์šฐ, LiveData๋Š” ๋“ฑ๋ก๋œ Observer ๊ฐ์ฒด์— ๋ณ€ํ™”๋ฅผ ์•Œ๋ ค์ฃผ๊ณ , Observer์˜ onChanged()๋ฉ”์†Œ๋“œ๊ฐ€ ์‹คํ–‰๋˜๊ฒŒ ๋œ๋‹ค.

 

 

LiveData๋Š” ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์–ด๋–ป๊ฒŒ ์•Œ๊นŒ?

LifeCycleOwner๊ฐ€ ์•ˆ๋“œ๋กœ์ด๋“œ ์ƒ๋ช…์ฃผ๊ธฐ(Android LifeCycle)์„ ์•Œ๊ณ  ์žˆ๋Š” ํด๋ž˜์Šค๋‹ค.
LifeCycleOwner๋Š” getLifeCycle() ๋ฉ”์„œ๋“œ๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋‹จ์ผ ์ธํ„ฐํŽ˜์ด์Šค ํด๋ž˜์Šค์ด๋ฉฐ Activity๋‚˜ Fragment์—์„œ ์ƒ์†ํ•˜๊ณ  ์žˆ๋‹ค.
์ฆ‰, LiveData์˜ Observer ๋ฉ”์„œ๋“œ์˜ LifeCycleOwner๋ฅผ Activity๋‚˜ Fragment์˜ ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๊ฐ ํ™”๋ฉด์˜ ์ƒ๋ช…์ฃผ๊ธฐ์— ๋”ฐ๋ผ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.

 

์žฅ์ 

UI์™€ Data์˜ ์ƒํƒœ ์ผ์น˜

Ensures your UI matches your data state

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

๋ฉ”๋ชจ์žฅ ์˜ˆ์ œ

๋ฉ”๋ชจ์žฅ ์•ฑ์—์„œ ๋ฉ”๋ชจ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋Š” ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•  ๋•Œ 

์‚ฌ์šฉ์ž๊ฐ€ ์ž์‹ ์˜ ๋ฉ”๋ชจ์žฅ ์ œ๋ชฉ์„ ์ˆ˜์ •ํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด,
1. DB์—์„œ ํ•ด๋‹น ๋ฉ”๋ชจ์˜ ์ œ๋ชฉ์„ ์ƒˆ๋กœ ๊ฐฑ์‹ ํ•œ๋‹ค.
2. DB์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์€ ๋ฉ”๋ชจ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋‹ค์‹œ ํ™”๋ฉด์— ๋ฟŒ๋ ค์ค˜์•ผ ํ•œ๋‹ค.

LiveData๋Š” DB์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒˆ๋กœ ๊ฐฑ์‹ ํ•˜๋ฉด ๋ฐ์ดํ„ฐ์˜ ๋ณ€ํ™”(Observer ๊ฐ์ฒด์˜ ๋ฐ์ดํ„ฐ ๋ณ€ํ™”)๋ฅผ ๊ฐ์ง€ํ•˜๊ณ  ์ž๋™์œผ๋กœ list์˜ ๊ธ€ ์ œ๋ชฉ์„ ๊ฐฑ์‹ ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— 2๋ฒˆ ๊ณผ์ •์„ ์ƒ๋žตํ•œ๋‹ค.

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋ฉ”๋ชจ๋ฅผ ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ์ถ”๊ฐ€ํ–ˆ์„ ๋•Œ, ๋ฉ”๋ชจ ์ œ๋ชฉ์„ ์กฐํšŒํ•˜์—ฌ list์— ์ƒˆ๋กญ๊ฒŒ ๋ฟŒ๋ ค์ฃผ๋Š” ์ฝ”๋“œ๋ฅผ ์“ฐ์ง€ ์•Š์•„๋„ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ „์ฒด ์ฝ”๋“œ๊ฐ€ ๊ฐ„๊ฒฐํ•ด์ง€๊ณ  ์œ ์ง€ ๋ณด์ˆ˜ํ•  ๋•Œ, ์‹ ๊ฒฝ์“ธ ๋ถ€๋ถ„์ด ์ค„์–ด๋“ ๋‹ค.

 

๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€  

No memory leaks

Observer ๊ฐ์ฒด๋Š” ์•ˆ๋“œ๋กœ์ด๋“œ ์ƒ๋ช…์ฃผ๊ธฐ ๊ฐ์ฒด์™€ ๊ฒฐํ•ฉ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ปดํฌ๋„ŒํŠธ๊ฐ€ Destroy ๋  ๊ฒฝ์šฐ ๋ฉ”๋ชจ๋ฆฌ์ƒ์—์„œ ์Šค์Šค๋กœ ํ•ด์ œํ•œ๋‹ค.
์ฆ‰, Activity ๋‚˜ Fragment ์˜ lifecylce์ด Destroyed ๋˜๋ฉด Observers๊ฐ€ clean up ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ๋ฐ ํšจ๊ณผ์ ์ด๋‹ค.

 

๋ฉˆ์ถ˜ ์•กํ‹ฐ๋น„ํ‹ฐ์™€ ์ถฉ๋Œ ๋ฐฉ์ง€

No crashes due to stopped activities

Lifecycle ์ค‘์— inactive (onStop, onDestroy) ๋œ activity ๊ฐ€ ์žˆ๋‹ค๋ฉด LiveData๋Š” ์–ด๋–ค ์ด๋ฒคํŠธ๋„ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค.
์ฆ‰, ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ Back Stack์— ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ Observer์˜ ์ƒ๋ช…์ฃผ๊ธฐ๊ฐ€ inactive(๋น„ํ™œ์„ฑํ™”) ์ผ ๊ฒฝ์šฐ, Observer๋Š” LiveData์˜ ์–ด๋–ค ์ด๋ฒคํŠธ๋„ ์ˆ˜์‹ ํ•˜์ง€ ์•Š๋Š”๋‹ค.

 

์ƒ๋ช…์ฃผ๊ธฐ์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ handling์„ ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. 

No more manual lifecycle handling

LiveData๋Š” ์ƒ๋ช…์ฃผ๊ธฐ๋ณ„๋กœ ์ž๋™์œผ๋กœ <UI component๊ฐ€ ๋ฐ์ดํ„ฐ์˜ ๋ณ€ํ™”๋ฅผ ์ธ์ง€ํ•˜๊ณ  ๊ฐฑ์‹ ํ•˜๋Š” ๊ฒƒ>์„ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ง์ ‘ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

 

์ตœ์‹  ๋ฐ์ดํ„ฐ ์œ ์ง€

ํ™”๋ฉด ๊ตฌ์„ฑ์ด ๋ณ€๊ฒฝ๋˜์–ด๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•œ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด, ๋””๋ฐ”์ด์Šค๋ฅผ ํšŒ์ „ํ•˜์—ฌ ์„ธ๋กœ์—์„œ ๊ฐ€๋กœ๋กœ ํ™”๋ฉด์ด ๋ณ€๊ฒฝ๋  ๊ฒฝ์šฐ์—๋„ LiveData๋Š” ํšŒ์ „ํ•˜๊ธฐ ์ „์˜ ์ตœ์‹  ์ƒํƒœ๋ฅผ ์ฆ‰์‹œ ๋ฐ›์•„์˜จ๋‹ค.

 

์ง€์›(Resource) ๊ณต์œ 

์ž์›(Resource)๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.
LiveData๋ฅผ ์ƒ์†ํ•˜์—ฌ ์ž์‹ ๋งŒ์˜ LiveDataํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ณ  ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ์ด์šฉํ•˜์—ฌ ์‹œ์Šคํ…œ ์„œ๋น„์Šค๋ฅผ ๋‘˜๋Ÿฌ์‹ธ๋ฉด(Wrap) ์•ฑ ์–ด๋””์—์„œ๋‚˜ ์ž์›์„ ๊ณต์œ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์‚ฌ์šฉ์‹œ ์ฃผ์˜ํ•  ์ 

๋‹ค์Œ์— ์ž‘์„ฑ ์˜ˆ์ •

 

๋ฐ˜์‘ํ˜•

์˜ˆ์ œ ์ฝ”๋“œ

๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์ˆซ์ž๋ฅผ ์ฆ๊ฐ€ํ•˜๋Š” ์•ฑ ์˜ˆ์ œ

MainActivity.kt

class MainActivity : AppCompatActivity() {

	// ์ „์—ญ ๋ณ€์ˆ˜๋กœ ViewModel lateinit ์„ธํŒ…
    private lateinit var viewModel : MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
		
        // ViewModel์„ ๊ฐ€์ ธ์˜จ๋‹ค
        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)

        findViewById<Button>(R.id.btnArea).setOnClickListener {
        	// ์นด์šดํŠธ ์ฆ๊ฐ€
            viewModel.plusLiveDataValue()
        }

		// LiveData๋ฅผ Observer๋ฅผ ์ด์šฉํ•ด ๊ด€์ฐฐํ•˜๊ณ 
        // ํ˜„์žฌ Activity ๋ฐ Observer๋ฅผ LifecycleOwner๋กœ ์ „๋‹ฌํ•œ๋‹ค.
        viewModel.testMutableLiveData.observe(this, Observer {
        	// ๋ณ€ํ™”๊ฐ€ ์žˆ์„ ๋•Œ UI(์—ฌ๊ธฐ์„  TextView)๋ฅผ ์—…๋ฐ์ดํŠธ ์‹œ์ผœ์ค€๋‹ค.
            findViewById<TextView>(R.id.textArea).text = viewModel.testMutableLiveData.value.toString()
        })

    }
}

MainViewModel.kt

class MainViewModel : ViewModel() {

	// LiveData์˜ ๊ฐ’์„ 0์œผ๋กœ ์ดˆ๊ธฐํ™”
    var testMutableLiveData = MutableLiveData(0)

    fun plusLiveDataValue(){
    	// LiveData์˜ ๊ฐ’ 1 ์ฆ๊ฐ€
        testMutableLiveData.value = testMutableLiveData.value!!.plus(1)
    }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/textArea"
            android:textSize="60dp"
            android:text="0"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <Button
            android:id="@+id/btnArea"
            android:text="btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>


    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

๋ฐ˜์‘ํ˜•

 

์ฐธ๊ณ 

https://velog.io/@jojo_devstory/Android-LiveData...%EB%84%8C-%EB%88%84%EA%B5%AC%EB%83%90

https://velog.io/@shin_stealer/LiveData-%EB%9E%80

https://developer.android.com/topic/libraries/architecture/livedata?authuser=1 

 

 

728x90
๋ฐ˜์‘ํ˜•