먼저 나는 Context라는 개념에 대해 사용만 했지 뭔가 확실히 누가 물어보면 답을 하기 애매했다.
그래서 한번 작성해보려고 한다.
먼저 문맥이란 뜻의 Context는 "어떤 객체를 핸들링하기 위한 접근 수단" 의미를 가진다고 한다.
좀더 설명하자면
일반적으로 멀티 테스킹을 지원하는 운영체제 에서 Task들은 운영체제가 정한 기준에 따라 작업을 번갈아가며 수행한다.
이떄, 해당 Task들의 수행상태를 기억하기 위해서 Task마다 실행 상태에 대한 정보를 구성하고 있는것들 Context라고 한단다.
음.....뭔가 이해가 조금씩 되고 있다.
자 그럼 Android 에서의 Context의 개념에 대해 알아보자.
Android는 여러가지의 실행 형태(4대 컴포넌트 = 액티비티,서비스 등)이 있다.
그래서 클래스 구현 상 중복된 정보들이 있을 수 밖에 없다. 이러한 정보들을 일원화 시키기 위해 실행 클래스들의 상위 클래스로서 Context 클래스를 제공하고 있다.
자 이제 이해가 어느정도 되었다.
그러니까 우리가 실행 상태에 따라서 그 안에 시점마다 각각 다른 상태의 정보를 부르는 것을 막기 위해서 사용하는것이다... 뭐 그런 말 처럼 느껴진다.
아아 그래서 위의 구조 처럼 Application인 전체 앱의 생명주기에 따른 ApplicationContext와 Activity Context 두 종류로 나눠서 스코프를 나누는 구나....이제 알았다...!!
그럼 당연히 ApplicationContext는 싱글톤 객체겠구나...!!!
자 그럼 또 궁금한것은
우리가 MVVM으로 개발하다보면 ViewModel이나 Model에서 Context가 필요한 경우가 있다. 그치만 애초에 참조도 되지 않고 구글에서도 부르는것을 권장하지 않는다고 한다....!! 왜??
공식 문서 내용
주의: ViewModel은 뷰, Lifecycle 또는 활동 컨텍스트 참조를 포함하는 클래스를 참조해서는 안 됩니다.
ViewModel 객체는 뷰 또는 LifecycleOwners의 특정 인스턴스화보다 오래 지속되도록 설계되었습니다. 이러한 설계로 인해 뷰 및 Lifecycle 객체에 관해 알지 못할 때도 ViewModel을 다루는 테스트를 더 쉽게 작성할 수 있습니다. ViewModel 객체에는 LiveData 객체와 같은 LifecycleObservers가 포함될 수 있습니다. 그러나 ViewModel 객체는 LiveData 객체와 같이 수명 주기를 인식하는 Observable의 변경사항을 관찰해서는 안 됩니다. 예를 들어 ViewModel은 시스템 서비스를 찾는 데 Application 컨텍스트가 필요하면 AndroidViewModel 클래스를 확장하고 생성자에 Application을 받는 생성자를 포함할 수 있습니다(Application 클래스가 Context를 확장하므로).
그 이유는 lifecycle에 있다.
먼저 위와 같이 ViewModel의 lifecycle이 Activity의 lifecycle보다 길다.
그러기 때문에 예를 들어 화면이 돌아가는 상황에 대해 생각해보자.
Viewmodel은 Activity가 destroy되는 시점을 고려하여 정보를 저장한다. 그런데 만약 detroy되기 전의 Activity의 상태를 받아놓은 상태에서 destroy 된 이후에 새로운 액티비티의 context와는 충돌이 발생할 것이다.(전의 context와 후의 context는 다른것이니까)
자 그럼 이제 우리는 그럼 어떻게 개발해야 하는가
우리는 굳이 Viewmodel에서 context를 사용해야 한다면 ViewModelProvider를 사용하는 것이 일반적인 방법이다.
ViewModel을 확장하여 Application을 참조하고 있는 AndroidViewModel이라는 것이 있는데 이것을 사용하여 Application Context를 사용하면 앞서 언급한 문제 없이 Context를 사용할 수 있다.
class MyViewModel(application: Application) : AndroidViewModel(application)
이러한 형태로 AndroidViewModel을 상속받아 class를 구현하고 context가 필요한 곳에서는 다음과 같은 방식으로 사용하면 된다.
getApplication<Application>().applicationContext
프로젝트 내 여러 ViewModel에서 Context가 필요할 때마다 위와 같이 작성하기 귀찮다면 다음과 같이 Extension 함수를 선언해두고 사용하는 것을 추천한다.
val AndroidViewModel.context: Context
get() = getApplication<Application>().applicationContext
그런데 여기서 구분해야 하는 것은 만약 이 context가 Activity 종속적이라면 비지니스 로직이라도 Activity에서 해주자