MVVM 구조로 안드로이드 개발을 진행하다 보면, ViewModel이나 Model에서 Context가 필요한 경우가 있다. 하지만, ViewModel의 Lifecycle이 Activity나 Fragment의 Lifecycle보다 길기 때문에 Activity/Fragment의 context를 ViewModel이 참조하고 있으면 안 된다. 왜 안 되는지에 대한 이유와 어떻게 사용할 수 있을지에 대해 정리해본다.
Lifecycle의 차이
예를 들면, 화면 rotation이 발생하면 Activity는 destroy되고 다시 생기는데, ViewModel은 이 경우에도 Activity가 완전히 종료되기 전까지는 유지된다. 그러므로 ViewModel이 Activity의 context를 받아서 계속 사용한다면, Activity가 이미 destroy된 경우에 잘못된 Context를 사용하게 된다. 이 경우 충돌이나 예외가 발생할 수 있으며, Context를 필드로 들고 있지 않더라도 함수로 넘겨받아 사용하는 것도 타이밍에 따라 문제가 발생할 수 있다.
ViewModel의 Lifecycle에 대한 공식 문서 내용
ViewModel 객체의 범위는 ViewModel을 가져올 때 ViewModelProvider에 전달되는 Lifecycle로 지정됩니다. ViewModel은 범위가 지정된 Lifecycle이 영구적으로 경과될 때까지, 즉 Activity에서는 Activity가 끝날 때까지, 그리고 프래그먼트에서는 프래그먼트가 분리될 때까지 메모리에 남아 있습니다. 위의 그림에서는 Activity가 회전을 거친 다음 끝날 때까지 Activity의 다양한 수명 주기 상태를 보여줍니다. 또한 관련 Activity 수명 주기 옆에 ViewModel의 전체 기간도 보여줍니다. 이 특정 다이어그램에서는 Activity의 상태를 보여줍니다. 동일한 기본 상태가 프래그먼트의 수명 주기에 적용됩니다. Activity 상태 변경에 따라 ViewModel의 수명 주기를 설명합니다. 일반적으로 시스템에서 Activity 객체의 onCreate() 메서드를 처음 호출할 때 ViewModel을 요청합니다. 시스템은 Acitivity 기간 내내(예: 기기 화면이 회전될 때) onCreate() 메서드를 여러 번 호출할 수 있습니다. ViewModel이 처음 요청되었을 때부터 Activity가 끝나고 폐기될 때까지 ViewModel은 존재합니다.
AndroidViewModel를 상속받아 Context 사용
MVVM구조에서 ViewModel을 사용할 때, ViewModelProvider를 사용하는 것이 일반적인 방법이다. 한 화면을 사용하는 동안 Activity가 파괴되고 다시 생성되더라도 savedInstance에 저장하는 것이 아니라 ViewModel에 내용이 유지되고 있어 해당 내용들을 이용하여 다시 그려주기만 하면 된다. ViewModel을 확장하여 Application을 참조하고 있는 AndroidViewModel이라는 것이 있는데 이것을 사용하여 Application Context를 사용하면 앞서 언급한 문제 없이 Context를 사용할 수 있다.
공식 문서 내용
주의: ViewModel은 뷰, Lifecycle 또는 활동 컨텍스트 참조를 포함하는 클래스를 참조해서는 안 됩니다.
ViewModel 객체는 뷰 또는 LifecycleOwners의 특정 인스턴스화보다 오래 지속되도록 설계되었습니다. 이러한 설계로 인해 뷰 및 Lifecycle 객체에 관해 알지 못할 때도 ViewModel을 다루는 테스트를 더 쉽게 작성할 수 있습니다. ViewModel 객체에는 LiveData 객체와 같은 LifecycleObservers가 포함될 수 있습니다. 그러나 ViewModel 객체는 LiveData 객체와 같이 수명 주기를 인식하는 Observable의 변경사항을 관찰해서는 안 됩니다. 예를 들어 ViewModel은 시스템 서비스를 찾는 데 Application 컨텍스트가 필요하면 AndroidViewModel 클래스를 확장하고 생성자에 Application을 받는 생성자를 포함할 수 있습니다(Application 클래스가 Context를 확장하므로).
위와 같이 ViewModel에 대한 공식 문서에서도 이러한 내용을 확인할 수 있다.
이러한 형태로 AndroidViewModel을 상속받아 class를 구현하고 context가 필요한 곳에서는 다음과 같은 방식으로 사용하면 된다.
프로젝트 내 여러 ViewModel에서 Context가 필요할 때마다 위와 같이 작성하기 귀찮다면 다음과 같이 Extension 함수를 선언해두고 사용하는 것을 추천한다.
참고
Stack Overflow에서도 이에 대해 논의된 내용이 있어서 링크를 남겨둔다. 이 포스팅에서 ViewModel이 Activity/Fragment의 Context를 참조하면 안 되는 이유가 명확히 이해되지 않는다면 아래 글을 읽어보는 것이 도움이 될 수 있다.
https://stackoverflow.com/questions/51451819/how-to-get-context-in-android-mvvm-viewmodel
'Android > Android Development' 카테고리의 다른 글
LiveData와 Kotlin의 Flow (956) | 2021.11.14 |
---|---|
DataStore of Android Jetpack (828) | 2021.10.24 |
Android LiveData setValue vs postValue (2) | 2021.10.21 |
Android Custom Lint Checks (1427) | 2021.08.22 |
Google I/O '21 (1785) | 2021.06.20 |
댓글