오늘은 구글의 Architecture Components에 들어가 있는,

LifeCycle과 ViewModel 그리고 LiveData에 대해서 정리해 보겠습니다.

양으로 보면 각각의 컴포넌트에 대해서 하나의 글로 정리하는 것이 좋지만,

서로 연결되어 있는 부분들이 많아서 같이 정리하는 것이 효율적일 것 같네요.

 

1. LifeCycle과 ViewModel 그리고 LiveData

이 세가지 Component를 사용하는 방법은 다음과 같은데요.

View를 담당하는 UI컨트롤러인 Activity나 Fragment를 최대한 가볍게 하고,

뷰의 데이터에 대한 부분은 ViewModel에게 맡기게 한다음,

데이터의 변화값을 관찰하고 변경하는 LifeData에게 맡기기 위함입니다.

이 과정에서 LifeCycle을 고려하여 데이터에서 뷰, 그리고 뷰에서 데이터로 가는 과정들이,

Memory Leak이나 App Crush를 최소화 할 수 있게 도와줄 것 이기 때문입니다.

그럼 이 컴포넌트들을 어떻게 사용할지 알아보도록 하겠습니다.

2. 라이브러리 import

라이브러리를 import하는 방법은 다음과 같습니다.

implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"

사실 세사지의 라이브러리를 import하는 것이므로 경우에 따라서는,

셋중에 하나만 사용할 수도 있으므로 구글에서는 이러한 경우들에 대응할 수 있도록 하고 있습니다.

2. LifeCycle

LifeCycle라이브러리는 라이프사이클을 고려하는 컴포넌트를 만들는데 도움을 주기위해,

class와 interface들을 제공해 줍니다.

라이프사이클을 고려하지 않는 코드들은 메모리의 leak이나 앱이 crash되도록 할 수 있습니다.

라이프 사이클에 따라 호출되는 메소드에 코드들이 분산되어 있는 경우가 많은데요.

 

LifeCycle은 안드로이드 LifeCycle과 state 정보를 가지고 있는 객체입니다.

구글의 공식 문서를 보면 Lifecycle에 대해 다음과 같은 그래프가 나오는데요.

 

 

위 이미지에서 중점적으로 봐야 할 것은, Events와 State입니다.

아래와 같이 표로 다시 정리해 보면, (당연하게도) Event가 변하는 동안 State도 변한다는 것인데요.

ON_CREATE 이벤트가 발생하고 나면 INITIALZED에서 CREATED로 변하게 되는 것 입니다.

LifeCycle은 이렇게 라이프사이클에 대한 구체적인 정보를 가지고 있는 객체입니다.

 

Event State
ON_CREATE INITIALIZED
ON_START CREATED
ON_PAUSE STARTED
ON_RESUME RESUMED
ON_STOP DESTROYED
ON_DESTROY  
ON_ANY  

먼저 이러한 정보를 활용하기 전에,

LifecycleOwner클래스를 보도록 하겠습니다.

 

2-1. LifeCycleOwner

이것은 라이프 사이클을 가지고 있는 객체를 위한 interface인데요.

유일하게 하나의 메소드인 getLifecycle()을 가지고 있습니다.

당연하게도, 이 메소드를 이용하면 lifecycle을 얻어올 수 있습니다.

그럼, lifecycle을 가지고 있는 것들은 무엇일까요?

AppCompatActivity, Fragment, ProcessLifecycleOwner, LifecycleService등이 있습니다.

AndroidX인 jetpack의 Acitivity들은 당연히 lifecycle을 가지고 있게 되겠지요.

 

ProcessLifecycleOwner는 조금 생소할 수 있는데요.

앱 전체의 process의 라이프사이클에 대한 정보를 가지고 있습니다.

 

 

2-2. LifeCycle활용 방법

A. LifecycleObserver 의 구현

라이프 사이클을 observe하기 위해서, interface인 lifecycleObserver를 구현하는 방법이 있는데요.

먼저 아래와 같이 LifecycleObserver를 구현해 줍니다.

 

 

이제 이 클래스를 Activity의 lifecycle에 observer를 붙여서 호출해 줍니다.

 

로그를 찍어보면 성공적으로 호출되는 것을 알 수 있습니다.

 

B. lifecycle을 넘겨서 사용

 

하나의 클래스가 LifecycleOwner의 interface인 LifecycleObserver를 구현한 다음,

자신의 메소드에 "@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)"와 같이

Annotation을 붙여 라이프사이클을 모니터링 할 수 있습니다.

게다가 isAtLeast라는 메소드를 이용하면,

특정 state를 지나지 않았을 때는 무언가를 못하도록 할 수도 있습니다.

 

 

C. Lifecycle을 활용할 준비가 되어 있는 라이브러리들에 lifecycle을 넘겨주기

위에서는 lifecycleObserver를 구현하거나, lifecycle을 받아서 직접 무언가를 했었는데요.

DataBinding클래스와 같이 라이프사이클을 고려하는 라이블러리에게 전달해주면,

이 라이브러리들이 Lifecycle에 따라 동작하므로,

메모리 Leak이나 App Crush가 더욱 적어질 수 있겠습니다.

예를 들면, DataBinding의 경우, 아래 한줄만으로 라이브러리가 해당 Activity의 Lifecycle에 따라서 동작하게 되는 것 이지요.

 

참고로, 해당 setter코드를 보면 아래와 같습니다.

 

 

2-4. lifecycle의 상태가 특정상태 이상일 경우에만 동작

Lifecycle라이브러리를 사용할 때 좋은 점 중 하나는,

현재의 상태가 일정 상태 이상인지를 알 수 있다는 것 인데요.

아래와 같이 isAtLeast라는 함수를 이용하면,

특정 상태 이상일 경우에 무언가를 할 수 있습니다.

 

 

2-5. 정리

LifeCycle을 고려해 동작하는 라이브러리나 클래스를 사용하는 것은 매우 중요한 것 같습니다.

사용방법도 복잡하지 않아서,

LifeCycle에 대한 정보를 가지고 있는 Lifecycle객체를 이용하면 되는데요.

Observer를 붙여주거나 혹은 lifecyle을 사용하는 라이브러리가 있다면, 넘겨주어서 활용하면 되므로

꼭 활용할 필요가 있겠습니다.

 

3. ViewModel

UI 데이터의 홀더로, UI를 위한 데이터를 제공해 주는 것이 바로 ViewModel인데요.

앞단에 Repository를 두어서,

직접 DB나 Rest API에 접근하지 않고,

Repository로부터 데이터를 가져옵니다.

(물론 이렇게 함으로서 코드를 조금은 더 써야 할 수는 있겠지만,

모듈화가 되어서 관리가 더욱 쉬워질 수도 있습니다.)

앱의 핵심인 비즈니스 로직이 들어가 있는 곳으로 매우 중요한 곳이라고 할 수 있겠습니다.

3-1. ViewModel클래스 생성

클래스의 이름은 model클래스의 이름을 따라가도 되고, View의 이름을 따라가도 될 텐데요.

View의 이름을 따라가도록 하겠습니다. 특히 하나의 View 컨트롤러(Activity 혹은 Fragment)에는

하나의 ViewModel이 매칭되어야 하기 때문입니다.

Fragment는 Fragment간 데이터 교환이 필요한 경우와 같이 ViewModel을 공유해야 하는 경우도 있을 수 있지만,

Activity의 경우 ViewModel을 공유하는 것은 좋은 방법이 아니라고 생각합니다.

 

 

3-2. Activity에서 가져오기

액티비티에서 가져올때는 ViewModelProviders클래스를 이용하는데요.

액티비티의 onCreate에서 아래와 같이 viewModel클래스를 가져올 수 있습니다.

kotlin에서 하던데로  데이터를  get혹은 set할 수 있습니다.

 

 

3-3. ViewModel의 lifecycle

ViewModel의 Lifecycle은 ViewControler의 라이프 사이클을 함께하다가,

activity가 finished되거나 혹은 fragment가 detach될 때

onCleared가 호출되며서 끝나게 됩니다.

 

이것은 굉장히 중요한 의미가 있는데요.

Activity의 라이프사이클이 변화하는 동안,

ViewModel은 꿋꿋이 살아남아서,

할일을 하고 onDestroy가 호출되고 나서, finished가 되서야  clear되는 것 인데요.

정말로 마지막까지 함께 한다는 표현이 맞을 것 같습니다.

 

 

그렇기 때문에, 절대로 Context를 ViewModel에 넣으면 않됩니다.

activity는 finish되었는데 ViewModel은 살아서,

메모리릭이 발생할 수 있기 때문입니다.

 

또한, Context가 필요한 경우에는 ViewModel클래스를 생성할 때,

ViewModel이 아니라 subclass인 AndroidViewModel클래스를 상속받으면 됩니다.

이렇게 하면 Application Context를 이용할 수 있기 때문입니다.

 

 

3-4. LiveData와 DataBinding과 함께 사용하기

ViewModel을 LiveData그리고 DataBinding과 함께 사용하면,

데이터가 바뀌면 바로바로 UI에 반영되는

Reactive한 UI를 만들 수 있습니다.

 

방법은 ViewModel에서 변수를 LiveData타입으로 변경해 주는 것인데요.

(LiveData에 대한 부분은 아래에서 자세히 정리하므로, 여기서는 구현만 간단히 하고 넘어 가겠습니다.)

또한 DataBinding에 관한 부분은 아래 링크를 참조해 주세요.

>> DataBinding에 대해서 알아보자 part1

 

먼저, LiveData타입의 student를 만들었습니다.

 

 

xml에서 variable을 만들어 dataBinding을 준비해 줍니다.

 

 

textView에서는 viewmodel이 hold하고 있는 student에 접근해서 name을 가져올 수 있지요.

 

 

이제 데이터 바인딩을 해 줄 차례입니다.

위에서 찾아왔던 viewModel을 viewmodel변수에 넣어주기만 하면 됩니다.

한가지 추가로 해야할 것은 lifecycleOwner를 지정해 주어야 한다는 점 입니다.

이렇게 하면 DataBinding이 lifecycle 을 observe하게 됩니다.

 

 

이렇게 binding된 ViewModel에서 data는 아래와 같이 set해주면 되는데요.

이렇게 객체를 넣어주면, 객체의 값이 변경될 때, Binding된 값이 UI에 반영이 됩니다.

Observer가 따로 필요하지 않는 것이지요.

 

 

3-5. 주의할 점들

 

MVVM패턴을 구현하면서,

ViewModel을 사용함에 있어서,

ViewModel을 Activity간에 공유해서는 않되는데요.

이유는 위에서 보시는 것처럼, Activity 종료시에

해당 ViewModel이 onCleared가 호출되버리기 때문입니다.

 

또한 ViewModel과 LiveData를 사용할 경우,

liveData가 background에 있다가 resume상태로 올라올 경우,

LiveData가 값을 다시 알려줄 수 있으므로,

무턱대고 값 변경시 Toast를 알려준다든가 하는 패턴은 좋지 못할 것 같습니다.

 

4. LiveData

이번에는 LiveData를 보도록 하겠습니다.

Livedata는 observe가 가능한 data홀더로 보면 되는데요.

어떤 데이터 객체가 값을 LiveData타입으로 set하면,

그 값은 이제 observe가 가능해지게 됩니다.

즉, 변화를 감지해서 반응할 수 있게 된다는 말이지요.

 

LiveData는 심지어 Lifecycle을 고려하여서 반응합니다.

notify받을 대상의 상태에 따라서 data를 흘려보내거나 멈추는데요.

이에 따라서 ui를 업데이트 하면서 발생할 수 있는

메모리릭이나 크래쉬도 막을 수 있습니다.

 

무엇보다도 viewModel을 사용할 때,

Retrofit와 OKHttp등을 이용해

비동기로 http접속을 해서 결과값을 받은 후,

그 결과값을 UI에 반영하려고 할 경우 LiveData를 이용하면,

observe하고 있다가 해당 결과 값이 나올 경우 손쉽게 이를 반영할 수 있다는 장점이 있습니다.

MVVM이 연결되는 핵심역할을 한다고 볼 수도 있는 것 이지요.

 

4-1. LiveData 구현

코드를 보면서 정리해 보겠습니다.

LiveData객체는 보통 ViewModel 클래스에서 사용하는데요.

ViewModel클래스가 바로 view에 그릴 데이터를 홀딩하고 있기 때문입니다.

위에서 ViewModel에 대해서 정리하면서 간단히 사용했었는데요.

 

 

위에서 MutableLiveData는 LiveData의 subclass인데요.

LiveData는 final같이 변경할 수 없는데, 변경할 수 없는 LiveData를 변경하기 위해서,

MutableLiveData를 이용한 것 입니다.

 

 

4-2. Observer 등록

이번에는 설정한 LiveData를 Observe하다가 업데이트 되면 UI에 반영할 수 있도록 등록을 해 보겠습니다.

먼저 ViewModel에 아래와 같이 LiveData를 설정하구요.

 

 

Activity나 Fragment에서 아래와 같이 Observer를 등록 해 주면 됩니다.

Observer를 등록하기 위해서 아래와 같이 LiveData에 observe라는 멤버함수를 사용하면 되는데요.

첫번째 인자로 Lifecycle Interface를 구현한 객체가 필요한 데요.

여기서는 Activity를 this로 넣어 주었습니다.

 

 

참고로 Fragment에서는 this가 아닌, viewLifecycleOwner를 첫번째 인자로 넣어 주어야 합니다.

(왜 viewLifecycleOwner가 필요한지는 아래 글의 마지막부분 Fragment의 DataBinding부분을 참조해 주세요.)

DataBinding에 대해서 알아보자 part1

 

이렇게 생성된 observer는 lifecycle에 바운드 되어 있으므로 MemoryLeak이나 null pointer exception생기지 않도록 도와줍니다.

 

 

 

이 LiveData는 active state과 inactive state이 있을 수 있는데요.

 

 

4-2. Transformation

A. Transfromation.map()

LiveData의 출력을 다르게 하고 싶을 때는 어떻게 해야 할까요?

이럴 때는 Transformations.map()메소드를 이용해 줍니다.

아래와 같이 사용할 수도 있겠지요.

 

 

B. Transformation.switchMap()

위의 코드를 봐서 알 수 있겠지만, 위에서는 Student의 이름이 나오게 되는데요.

다시 Student객체 타입으로 변경하려면 switchMap()을 사용해 주면 됩니다.

 

5. Data Binding RecyclerView 그리고 LiveData

LiveData가 정상적으로 동작하기위해서는 LifecycleOwner를 필요로 합니다.

그런데, RecyclerView는 그것을 가지고 있지 않아서 ViewHolder에서 구현해 주어야 하는데요.

먼저 ViewHolder에서 LifecyclerOwner를 구현해 주어야 합니다.

Android에서는 Custom하게 lifecycle을 등록하기 위해서,

LifecycleRegistroy클래스를 제공해 주는데요 d이 클래스를 이용해서 아래와 같이 등록해 주구요.

markAttach와 markDetach함수를 만들어서,

currentState를 set할 수 있도록 해 줍니다.

 

 

Adapter에서는 아래와 같이 위에서 만들었던 markAttach와 markDetach를

onViewAttachedToWindow와 Detached에서 다음과 같이 사용해 줍니다.

이 부분은 매우 중요해서, 이 부분이 구현이 않되면,

DataBinding라이브러리가 lifecycle을 제대로 인지할 수 없으므로,

notifyDataSetChanged에 제대로 UI가 갱신이 않되는 일이 발생할 수 있습니다.

 

마지막으로는 Adapter의 onCreateViewHolder함수에서 Binding의 lifecycleOwner를 지정해 주면 됩니다.

 

 

이렇게 해서 Data Binding과 LiveData를 RecyclerView에서도 활용하는 방법을 정리해 보았습니다.

 

6. 정리

아주 좋은 툴처럼 보이는 LiveData이긴 하지만,

주어진 조건에 따라 활용하는 것이 중요할 것 같습니다.

왜냐하면, 어떤 경우에는 무조건 업데이트 되는 최신 정보를 보여주는 것이 아니라,

조건을 만들고 그 조건을 충족할 경우에만 업데이트 되도록 해야하기 때문인데요.

이러한 경우에는 LiveData가 않맞을수도 있기 때문입니다.

 

+ Recent posts