Android AAC-ViewModel 대신 Lifecycle을 적용해 보자.



개인 광고 영역

Android ViewModel에서 Flow를 이용한 View Controller 방법과 UnitTest 가능한 형태를 살펴보았습니다.

이번 글에서는 Lifecycle에 알아서 반응하여, 초기화할 수 있는 형태로 접근해 보려고 합니다.

이번 글에서는 Android AAC Lifecycle을 활용해 보겠습니다.


이 글에서 알아볼 내용

  • Lifecycle에 따라 함수 호출을 없애보자.
  • AAC Lifecycle을 알아본다.
  • AAC ViewModel 대신 Lifecycle을 적용해 본다.


AAC-ViewModel

Android 개발에서는 AAC-ViewModel을 보통 활용하고 있습니다. 하지만 Rotation에 반응할 필요 없다면 AAC-ViewModel을 굳이 사용할 필요는 없습니다.

그래도 사용하면 편리하니 많이 사용하고 있습니다.

AAC-ViewModel은 onCreate 시점에 초기화하고, 로테이션 예외를 제외하면 onDestroy에서 clear() 동작합니다.

AAC-ViewModel에서 사용하는 viewModelScope 역시 clear()에 cancel() 하도록 구현되어 있습니다.

Android Architecture Components ViewModel을 간단하게 초기화 하려면?

이 글에서는 AAC-ViewModel 대신 AAC Lifecycle을 사용합니다.


AAC Lifecycle

Lifecycle은 Activity/Fragment의 Lifecycle 객체를 활용합니다.

onCreate, onStart, onResume에서 자동으로 함수를 호출해 동작할 수 있습니다. 모든 Lifecycle을 알 수 있기 때문에 사용이 쉽습니다.

사용은 간단한데, DefaultLifecycleObserver을 상속받습니다.

lifecycle 2.4.0에서 @OnLifecycleEvent는 Deprecated 되었고, DefaultLifecycleObserver or LifecycleEventObserver을 사용해야 합니다.

디펀던시 추가

// alternately - if using Java8, use the following instead of lifecycle-compiler
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

적용 코드

class LifecycleSample : DefaultLifecycleObserver {

    override fun onCreate(owner: LifecycleOwner) {
        // 구현
    }

    override fun onDestroy(owner: LifecycleOwner) {
        // 구현
    }
}

그리고 이 코드가 동작할 수 있도록 lifecycle Observer에 등록합니다.

override fun onAttach(context: Context) {
    super.onAttach(context)
    lifecycle.addObserver(LifecycleSample())
}


Lifecycle을 적용

기존 AAC-ViewModel을 활용하던 ViewModel을 DefaultLifecycleObserver로 변경하는 작업을 합니다.

class SecondViewModel : ViewModel() {

    // load 함수는 제거하고, viewController을 생성자로 받습니다.
    fun load(viewController: OnClickEventControl) {
        flowButtonSecond(viewController)
            .launchIn(viewModelScope)


        flowButtonPlusCount(viewController)
            .launchIn(viewModelScope)
    }
}

위 코드를 아래와 같이 변경합니다. viewModelScope을 사용할 수 없기 때문에 CoroutineScope도 하나 다시 만들어야 합니다.

  • DefaultLifecycleObserver : 라이프 사이클에 따라 동작하도록 합니다.
  • CoroutineScope : viewModelScope을 활용할 수 없기 때문에 새로 생성합니다.
  • load 함수에서 viewController을 받던 부분을 제거하고, 생성자로 우선 받습니다.

이 코드는 어디까지나 간단하게 생각하고 접근하기 위한 코드입니다. 완전하지는 않지만 방법론에 집중해 주시면 좋겠습니다.

class SecondViewModel(
    private val viewController: OnClickEventControl
) : DefaultLifecycleObserver {

    // viewModelScope을 대신할 CoroutineScope을 추가
    private val coroutineScope = object : CoroutineScope {

        override val coroutineContext: CoroutineContext
            get() = Dispatchers.Main + SupervisorJob()
    }

    // load() 함수에서 하던 부분을 onCreate()가 호출되면 동작하도록 변경합니다.
    override fun onCreate(owner: LifecycleOwner) {
        flowButtonSecond(viewController)
            .launchIn(coroutineScope)


        flowButtonPlusCount(viewController)
            .launchIn(coroutineScope)
    }

    // viewModelScope을 사용할 수 없기 때문에 cancel을 별도로 처리합니다
    override fun onDestroy(owner: LifecycleOwner) {
        coroutineScope.coroutineContext.cancel()
    }


이 코드는

이 코드는 GitHub에 업로드해두었으니 참고하시면 되겠습니다.

GitHub ViewModel Lifecycle 샘플 코드


마무리

lifecycle까지 적용하면 이제 라이프 사이클에 따라 함수가 호출되고, 라이프 사이클에 따라 coroutines이 종료됩니다.

사실 AAC-ViewModel을 사용했을 때와 큰 차이는 없지만, Activity에서 viewModel을 접근하던 아래 코드가 제거되었음을 알 수 있습니다.

viewModel.load(viewController) // 제거

최종적인 목표는 이 코드는 아니겠지만, 좀 더 유연하게 동작하는 코드가 만들어진 것 같은 느낌이 들면 좋겠습니다.

  • Lifecycle에 대응하는 자동적인 함수 호출
  • Flow를 활용함으로 Stream으로 View를 컨트롤하고, 처리할 수 있다.
  • launchIn의 분리로, 함수별 UnitTest를 진행할 수 있다.

AAC-ViewModel이 워낙 쉽게 접근하고 사용할 수 있어 Lifecycle의 적용이 큰 이점은 없을 수 있습니다.

하지만 모든 함수가 onCreate 시점에 불리는 것은 아니고, onResume에서 다시 동작하거나, 그 시점에서만 데이터를 갱신해야 하는 등의 일이 생길 수 있습니다.

Stream을 적절하게 사용한다면 onResume에서 대응되어야 할 부분은 없겠지만요.



About Taehwan

My name is Taehwan Kwon. I have developed Android for 6 years and blog has been active for eight years.

Comments