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에서 대응되어야 할 부분은 없겠지만요.
Comments