💡 Intro
안드로이드에서 중요한 요소 중 하나인 컨텍스트. 컨텍스트가 없으면 안드로이드에서는 아무것도 할 수 없다. 그만큼 중요한 요소지만 나는 얼마나 알고있을까? 에 대해 생각해봤고, 깊은 이해가 있다고 생각되지 않기 때문에 정리하게 되었다.
❓ Context
공식문서를 보면 컨텍스트를 다음과 같이 정의한다.
Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.
영어는 어려우니 정리해보자.
- 컨텍스트는 어플리케이션 환경에 대한 전역 정보의 인터페이스다.
- 컨텍스트는 추상 클래스이고, 안드로이드 시스템이 구현을 제공한다.
- 컨텍스트는 어플리케이션의 리소스와 클래스에 접근할 때 사용된다.
- 컨텍스트는 시스템 요청이 필요한 작업에 대한 API를 제공한다. (액티비티 실행, 브로드캐스팅, 인텐트 수신 등)
컨텍스트를 통해 Resource, DataBase, SharedPreferences 등의 시스템 자원을 사용할 수 있다.
Context는 각각의 Application, Activity에 대한 정보와 기능에 대한 접근 권한을 가진 객체라고 볼 수 있다.
아래는 Context 클래스가 가지고 있는 메서드 중 일부인데, 이를 통해 어떤 일을 하는지 감을 잡을 수 있을 것 같다.
// 어플리케이션에 관해 시스템이 관리하고 있는 정보에 접근
public final String getString(@StringRes int resId, Object... formatArgs)
public final int getColor(@ColorRes int id)
public final Drawable getDrawable(@DrawableRes int id)
public abstract String getPackageName();
public abstract SharedPreferences getSharedPreferences(String name, @PreferencesMode int mode);
// 같은 이름에 대해 오직 하나의 SharedPreferences객체의 인스턴스 하나만 반환되므로 서로의 편집 내용을 즉시 볼 수 있음
// Thread-safe 한 메서드
// Preferences directory가 이미 존재하는 상태가 아니라면 이 메서드가 호출될 때 생성됨
// 안드로이드 시스템에서 제공하는 API
public abstract void startActivity(@RequiresPermission Intent intent);
Context는 크게 두 가지 역할을 수행하는 추상 클래스다.
- 어플리케이션에 관하여 시스템이 관리하고 있는 정보에 접근 ex. getString(), getColor(), getDrawable(), getPackageName(), getSharedPreferences()
- 안드로이드 시스템 서비스에서 제공하는 API를 호출 할 수 있는 기능 ex. startActivity(), bindService()
❓ Context가 필요한 이유
컨텍스트가 무엇인지 감을 잡았다. 그럼 이 친구가 왜 필요할까?
안드로이드는 어플리케이션과 프로세스가 별도로 관리된다. 일반적인 경우와 마찬가지로 각각의 프로세스는 OS의 커널에 의해 관리되지만, 각각의 어플리케이션은 ActivityManagerService에 의해 관리된다.
어플리케이션의 정보를 관리하는 주체는 프로세스가 아니라 ActivityManagerService인 것이다. ActivityManagerService는 Key-Value 쌍으로 이루어진 배열을 이용해 현재 작동중인 어플리케이션의 정보를 관리한다.
컨텍스트는 어플리케이션과 관련된 정보에 접근하고자 하거나 어플리케이션과 연관된 시스템 레벨의 함수를 호출하고자 할 때 사용된다. 하지만 어플리케이션의 정보에 대한 관리는 시스템이 아닌 ActivityManagerService가 하고있기 때문에 어플리케이션에 대한 정보에 접근하고자 할 때는 ActivityManagerService를 통해야 한다. 그리고 ActivityManagerService가 Key-Value의 형태로 어플리케이션의 정보를 관리한다고 했기 때문에 어떤 어플리케이션인가에 대한 Key값도 필요하다.
ActivityManagerService 입장에서는 자신에게 들어오는 정보 접근 요청이나 시스템 함수 호출 요청이 어떤 앱의 요청인지 파악해야 하고, 이 때 컨텍스트를 사용한다. 즉, 컨텍스트는 자신이 어떤 어플리케이션인지 알려주는 ID 역할을 한다고 볼 수 있다.
❓ Activity Context
액티비티 컨텍스트는 이름에서도 알 수 있듯이 액티비티의 생명주기와 연결되어 있다. 액티비티 범위 내에서 컨텍스트를 전달할 때나 액티비티에 연결된 생명주기를 가진 객체에서 사용한다.
액티비티 컨텍스트를 가지고 할 수 있는 것들은 다음과 같다.
- 리소스 값 로드
- 서비스 시작 및 바인드
- 브로드캐스트 전송 및 리시버 등록
- 레이아웃 인플레이트
- 액티비티 시작
- 다이얼로그
❓ Application Context
어플리케이션 컨텍스트는 싱글턴 인스턴스이며, 어플리케이션의 생명주기와 연결되어 있다. 어플리케이션의 생명주기와 연결되어 있다는 말은 앱의 시작부터 종료까지 살아있다는 말과 같다.
어플리케이션 컨텍스트를 가지고 할 수 있는 것들은 다음과 같다.
- 리소스 값 로드
- 서비스 시작 및 바인드
- 브로드캐스트 전송 및 리시버 등록
❓ 어떤 Context를 사용해야 해
그럼 언제 어떤 컨텍스트를 사용해야 할까?
일단 가장 먼저 나눌 수 있는 것은 액티비티 컨텍스트, 어플리케이션 컨텍스트를 통해 할 수 있는 것들이다. 액티비티 컨텍스트를 통해 다이얼로그를 띄울 수 있지만, 어플리케이션 컨텍스트로는 불가능하다.
액티비티를 시작하는 것 역시 액티비티 컨텍스트를 사용해야만 한다.
다음으로는 가장 가까운 컨텍스트를 사용하는 것이다.
컨텍스트를 사용할 때는 자신과 연관된 컨텍스트를 참조하는 것이 안전하다. 이러한 이유 때문에 내가 사용하는 컨텍스트의 범위와 이 컨텍스트가 줄 수 있는 영향력의 범위를 알고 있는 것이 중요하다.
❓ Memory Leak
메모리 누수란 프로그램에서 동적으로 할당된 메모리가 제대로 해제하지 않아 사용하지 않는 메모리가 쌓이는 것을 의미한다. 안드로이드에서는 인스턴스가 사용되지 않는다고 판단되면 가비지 컬렉터가 해당 인스턴스를 수거하고 차지하고 있던 메모리를 회수한다.
메모리 누수가 발생할 수 있는 경우가 다양하게 있지만 컨텍스트를 올바르게 사용하지 못할 때도 발생할 수 있다. 만약 어플리케이션에서 전체적으로 사용하는 경우 어플리케이션 컨텍스트를 사용해야 한다. 만약 A액티비티의 컨텍스트를 사용하는 경우 해당 A액티비티가 종료되더라도 다른 곳에서 A액티비티의 컨텍스트를 참조하고 있기 때문에 메모리에서 해제되지 않는다.
그럼 어플리케이션 컨텍스트를 사용하면 메모리 누수가 발생하지 않는가? 그것은 아니다. 액티비티나 프래그먼트에서 어플리케이션 컨텍스트와 강한 참조를 유지하고 있는 경우 해당 객체가 필요하지 않은 상황에서도 계속 메모리에 남아있을 수 있다.
참고