Interface to global information about an application environment.
어플리케이션 환경에 대한 전역 정보에 대한 인터페이스입니다.
Context 의 잘못된 사용은, 메모리 릭 문제로 이어질 수 있기 때문에 정말 주의해야 한다.
이러한 Context 는, 크게 나누자면 또 두 가지로 나눌 수 있다.
Activity 에서 applicationContext 라는 프로퍼티를 통해 얻을 수 있는 (코틀린 기준, 자바의 경우 getApplicationContext() 라는 메소드를 통해 얻을 수 있음) 싱글톤 인스턴스이다.
이 Context 는 어플리케이션 라이프사이클과 묶여있어, 현재 Context 가 종료되고 나서도 Context 가 필요한 작업이나, 액티비티 범위를 벗어난 곳에 Context 가 필요한 작업에 적합하다.
예를 들어, 어플리케이션 내에 싱글톤 객체를 만드려고 하는데 이 객체가 Context 를 필요로 할 때, Application Context 를 사용하면 된다. 만약 이런 상황에 Activity Context 를 넘겨주게 되면, Activity 에 대한 참조를 메모리에 남겨두며 GC (Garbage Collected) 되지 않은 채 분명 메모리 릭이 발생할 것이다.
그럼, 어플리케이션 전역에서 사용할 어떤 라이브러리를 MainActivity 에서 초기화 할 때 Context 가 필요하다고 가정해보자. 어떤 Context 를 넘겨줘야 할까?
정답은 Application Context 이다. 만일 Activity Context 를 넘겨주게 되면, MainActivity 에 대한 참조가 메모리 상에서 GC 되지 않아 메모리 릭이 발생하기 때문이다.
따라서, 오랫동안 지속되거나 앱 전역에서 사용될 녀석의 경우 Application Context 를 넘겨주면 된다.
해당 Context 는 액티비티 안에서만 사용 가능하다. 특정 Activity 의 라이프 사이클에 종속되어 있다. 이 녀석은 Activity 스코프 내에서 사용될 때 넘겨주거나, Activity 와 라이프사이클이 같은 객체를 생성할 때 넘겨준다. 즉, Activity 가 소멸되면 해당 Context 도 같이 소멸되는 것이다.
그렇다면, 각각 컴포넌트의 입장에서 Context 의 사용 가능 여부를 살펴보자.
MyApplication : Application Context 사용 가능
MainActivity1 : Application Context, Activity 1 Context 사용 가능
MainActivity2 : Application Context, Activity 2 Context 사용 가능
Application Context 는 Activity Context 가 지원하는 모든 것을 지원하지 않는다. 만능처럼 보이지만 절대 아니다. 따라서 GUI (View Component 등) 관련 동작들에 있어 Application Context 는 오류를 발생할 확률이 높다.
예를들어, Application Context 를 활용하여 AlertDialog 를 show() 하게 되면
java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
과 같은 오류를 맛볼 수 있다. 따라서 조심하여 사용하도록 하자.
Activity 는 Garbage Collection 이 가능하지만, Application 은 앱 프로세스가 살아있는 동안 계속하여 남아있다. 따라서, Application Context 를 활용한 객체를 메모리에서 할당 해제하지 않고 있을 경우, 메모리 릭을 발생할 가능성이 농후하다.
👮🏻♀️ Q1. MyApplication 클래스와 MyDB 싱글톤 객체가 있을 때, MyDB 가 Context 를 필요로 하는 경우, 어떤 Context 를 넘겨줘야 할까?
정답: Application Context
만약 Activity Context를 전달했다면, 액티비티가 사용되지 않을 때에도 MyDB가 해당 액티비티를 참조하고, 메모리 릭이 발생할 것이다. 따라서, 싱글톤 객체에서 Context 를 필요로 하는 경우, 무조건 Application Context 를 사용해야 한다. 필수다!
👮🏻♀️ Q2. Toast , Dialog 등의 UI 동작에 있어 Context 가 필요하다면, 어떤 Context 를 넘겨줘야 할까?
정답: Activity Context
해당 UI 컴포넌트들은 어차피 Activity 의 라이프 사이클에 종속되는 것들이기 때문에, Activity Context 를 사용해주면 된다.
항상 가장 가까운, 밀접한 스코프의 Context 를 골라 사용하면 된다. 액티비티에서 사용된다면 Activity Context 를, 어플리케이션 전역에서 (싱글톤 등) 사용된다면 Application Context 를 사용하면 된다. 이렇게만 사용하면 메모리 릭 걱정은 없다.
class MainActivity : AppCompatActivity() {
init {
instance = this
}
companion object {
lateinit var instance: MainActivity
fun getContext(): Context {
return instance.applicationContext
}
}
}
object StoreItemsLocalDataSource : StoreItemsDataSource {
override fun getStoreItems(): MutableList<MainViewData> {
val context = MainActivity.getContext()
val mainStoreItem = getJsonString(context.assets.open(ItemType.MAIN.fileName))
}
}