반응형

LiveData의 정의


 

ViewModel은 그 내부에서 LiveData를 사용하고 있다. 그러므로 우선 LiveData에 대해서 알아보고자 한다.

안드로이드 개발자 공식 홈페이지에서 설명하고 있는 LiveData는 아래와 같이 정의되어 있다. (링크)

 

LiveData is a data holder class that can be observed within a given lifecycle.

This means that an Observer can be added in a pair with a LifecycleOwner,

and this observer will be notified about modifications of the wrapped data

only if the paired LifecycleOwner is in active state.

 

안드로이드 라이프사이클에서 관찰될 수 있는 데이터 홀더 클래스라고 소개되고 있다.

또한, Observer는 LifecycleOwner와 함께 짝을 지어 추가될 수 있고,

LifecycleOwner가 활성 상태일 때 데이터가 변경되면 Observer에게 알려준다고 써져 있다.

즉, 쉽게 말해서 데이터의 값이 변경되었을 때, 이를 등록한 Observer에게 Notify해주는 클래스라고 생각하면 된다.

 

 

 

 

 

 

LiveData를 사용하는 이유는?


 

LiveData를 사용하면 다음과 같은 이점이 있다.

 

(1) UI와 데이터 상태의 일치 보장

    LiveData는 기본적으로 Observer Pattern을 따르고 있기 때문에, 데이터가 변경되면 Observer 객체에 이를 알린다.

    UI의 업데이트는 Observer로부터 받은 데이터를 통해 업데이트하는 구조이기 때문에, 데이터 상태가 일치함이 보장된다.

 

(2) 메모리 누수 없음

    Observer는 Lifecycle 객체에 연결되어 있고, 이에 따라 라이프 사이클이 종료되는 순간 자동으로 삭제된다.

    생성과 삭제가 라이프 사이클에 의존하기 때문에 메모리의 누수가 발생하지 않는다.

 

(3) 비정상적인 Crash를 발생시키지 않음

    만약 Activity가 비활성 상태에 있다면, Observer는 어떤 LiveData 이벤트도 받지 않는다.

    그러므로 비정상적인 데이터 수신으로 인한 Application Crash는 발생하지 않는다.

 

(4) 리소스의 공유

    하나의 LiveData에 다수의 Observer가 연결될 수 있고, 이를 통해서 리소스를 쉽게 공유할 수 있다.

 

 

 

 

 

 

ViewModel 정의


 

안드로이드 개발자 공식 홈페이지에 나와있는 ViewModel의 정의는 다음과 같다. (링크)

 

The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way.
The ViewModel class allows data to survive configuration changes such as screen rotations.

 

말 그대로, ViewModel은 UI와 관련된 데이터들을 저장하고 관리하기 위해, 라이프사이클을 고려하는 방향으로 설계되었고,

가로/세로 화면 전환과 같은 변화에 무관하게 값이 일관되도록 만들어 준다는 것이다.

화면 전환의 예시는 ViewModel을 사용하는 이유를 굉장히 잘 보여주는 사례라고 할 수 있는데,

다음과 같이 세로 화면에서 초기화 이후 변경되었던 값이 가로 화면으로 변경되었을 때 다시 초기화된 값으로 변경되는 경우라고 할 수 있겠다.

(이는 onCreate() 메소드가 다시 불려지면서 초기값이 지정되었기 때문이다.)

 

 

 

 

이렇게 UI 관련 데이터가 외부 요인에 의해서 삭제되지 않도록,

Class의 라이프사이클과는 독립적으로 데이터가 유지되는 ViewModel이라는 테크닉이 필요한 것이다.

Google에서는 개발자들이 손쉽게 ViewModel을 구현할 수 있도록 라이브러리를 제공하고 있다.

 

 

 

 

 

 

ViewModel Lifecycle


 

윗 문단에서 ViewModel은 Activity와는 독립적으로 운용된다고 하였다.

즉 Activity의 Lifecycle과는 무관하게 ViewModel의 Lifecycle을 가진다는 의미인데,

Google에서는 이에 대한 이해를 돕기 위한 다음과 같은 이미지를 제공한다.

 

 

이미지를 보면 알 수 있듯이,

ViewModel은 Activity가 모두 종료된 이후, onCleared 메소드를 통해서야 비로소 소멸이 된다.

즉 Activity보다 ViewModel의 Lifecycle이 더 길다고 할 수 있고,

이는 결국 Activity의 Lifecycle과는 무관하게 ViewModel이 데이터를 유지할 수 있도록 해준다.

 

 

 

 

 

ViewModel 사용법


 

(1) Library Import

 

ViewModel을 사용하기 위해서는, 가장 먼저 'build.gradle' 파일에 아래와 같이 관련 라이브러리를 선언해주어야 한다.

하나는 LiveData를 사용하기 위해, 다른 하나는 ViewModel을 사용하기 위한 라이브러리이다.

본 문서에서는 2.3.0 버전을 사용할 것이다. (22/01/03 기준 최신 버전은 2.4.0)

 

dependencies {
    def lifecycle_version = "2.3.0"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
}

 

 

(2) ViewModel Class 작성

 

다음으로는 사용하고자 할 Class를 작성해주어야 한다.

본 문서에서는 ViewModel의 이해를 위해서, String을 입력하고 저장하면 TextView에 표시되는 간단한 예제를 고려할 것이다.

 

우선 다음과 같이 ViewModel을 위한 Class를 작성한다.

여기에서는 NameViewModel 이라는 이름으로 작성해주었다.

 

 

class NameViewModel : ViewModel() {
    val currentName: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }
}

 

 

currentName 이라는 변수가 값을 Observe할 대상으로 만들어질 것이다.

여기에서 MutableLiveData 라는 클래스가 쓰였는데, 이 클래스는 Observer Pattern과 비슷한 역할을 가지는 클래스이다.

ViewModel에서 사용하는 데이터는 이 클래스의 생성자를 통해 만든다고 이해하면 된다.

 

 

(3) Layout 파일 작성

 

다음으로는 Layout 파일을 작성할 차례이다.

여기에서 주의할 점은, 일반적인 레이아웃 (LinearLayout 등) 이 <layout> 태그 안으로 들어가야 한다는 것이고,

Observe할 대상을 <data> 태그를 통해 작성해야 한다는 것이다.

아래의 코드를 보자.

 

 

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="name"
            type="com.example.viewmodel_exam.NameViewModel"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="10dp"
        android:gravity="center">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <EditText
                android:id="@+id/editName"
                android:layout_weight="3"
                android:hint="Type your name"
                android:layout_width="0dp"
                android:layout_height="wrap_content"/>

            <Button
                android:id="@+id/btnSave"
                android:layout_weight="1"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:text="SAVE"/>
        </LinearLayout>

        <TextView
            android:id="@+id/textOutput"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"/>
    </LinearLayout>
</layout>

 

 

(4) Activity 작성

 

마지막으로 Activity를 작성할 차례이다.

ViewModel을 사용하기 위해서는 ViewModelProvider 라는 클래스의 도움을 받아야 한다.

ViewModelProvider에 ViewModel의 소유자를 명시하고, get 메소드를 호출함으로써 ViewModel을 얻을 수 있다.

 

이렇게 획득한 ViewModel의 MutableLiveData에 대해서 observe 메소드를 호출하면

바인딩이 이루어지고, 이를 통해서 값이 변경되었을 때 이를 UI에 적용되도록 작성할 수 있다.

아래의 코드를 보면 이해가 빠를 것이다.

 

 

class NameActivity : AppCompatActivity() {
    private lateinit var model: NameViewModel
    private lateinit var binding: ActivityNameBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_name)
        model = ViewModelProvider(this).get(NameViewModel::class.java)
        binding.name = model

        model.currentName.observe(this, Observer {
            binding.textOutput.text = it.toString()
        })

        binding.btnSave.setOnClickListener {
            binding.textOutput.text = binding.editName.text.toString()
            binding.editName.text.clear()
        }
    }
}

 

 

반응형
복사했습니다!