[Android] Context

혜령·2022년 2월 14일
0

Android 공부하기

목록 보기
6/8

Context란?


Android에서는 Context를 사용하게 됩니다. Context가 필요한 경우에는 Context를 얻어서 사용했습니다. 이런 Context에도 종류가 있다는 것을 알게 되어서 더 자세히 알아보고자 합니다.

Android Developer 공식 문서의 설명을 보면 다음과 같습니다.

Context는 애플리케이션 환경에 대한 글로벌 정보를 갖는 인터페이스입니다. Context는 Android 시스템에서 구현체를 제공하는 추상 클래스로, 애플리케이션 별 리소스 및 클래스 접근에 사용되며, 액티비티 실행, 브로드캐스트, 인탠트 수신 등과 같은 애플리케이션 수준 작업에 사용됩니다.

위에서 설명하는 것을 쉽게 말해보자면, Context는

  • 어플리케이션에 관한 정보를 가지고 제공해줄 수 있습니다.
  • 안드로이드 시스템 서비스에서 제공하는 API를 호출할 수 있도록 합니다.

두 가지 역할을 합니다.

어플리케이션에 관한 정보를 제공한다는 것을 먼저 살펴봅니다.

Context는 Resource, Database, Shared preference 등의 시스템 자원에 접근할 수 있게 해주는 시스템의 핸들과도 같습니다. 또한, Context는 새로 생성된 객체가 지금 어떤 일이 일어나고 있는지에 대한 정보를 알 수 있도록 합니다. 여기서 정보란 어플리케이션 패키지 이름, 리소스 정보 들 어플리케이션이 실행되고 있는 환경 요소들을 나타냅니다. Context가 제공하는 메서드 중 getPackageName(), getResource() 등이 이 역할을 하고 있습니다.

안드로이드 시스템 서비스에서 제공하는 API를 호출하는 것을 살펴봅니다.

Context를 통해서 Activity를 시작하거나, 브로드캐스트를 발생시키고, 서비스를 실행할 수 있습니다. startActiviy()나 bindService()와 같은 메서드가 그런 역할을 수행합니다.

Activity, Application, Service 클래스는 Context 클래스를 확장한 클래스입니다. Context를 상속받아서 Context의 기능을 이용합니다.

엄밀히 말하면, Context 추상 클래스의 메서드를 직접 구현한 것은 ContextImpl라는 클래스입니다. ContextWrapper 클래스가 ContextImpl을 내부적으로 이용하고 있습니다. 실제로 애플리케이션(Application), 액티비티(Activity), 서비스(Service)는 Context 추상 클래스를 상속받는 것이 아니라 ContextWrapper 클래스를 상속받습니다.

각각의 컴포넌트들은 Application Context도 가지고 있습니다. 이 Context를 이용해도 되지만, 시스템 API를 각 컴포넌트에 맞게 구현할 필요가 있습니다. 액티비티와 서비스는 각자의 동작 방식이 있습니다. 따라서 각 컴포넌트의 역할에 맞게 API를 사용하도록 Context를 상속받습니다.

Context가 필요한 이유


일반적인 OS와는 다르게, 안드로이드는 어플리케이션과 프로세스가 독립적입니다. 따라서 프로세스가 종료되어도 어플리케이션은 실행 중일 수 있습니다. 예를 들어, Service나 Broadcast Receiver가 어플리케이션 실행 여부와 무관하게 백그라운드에서 실행 가능합니다.

이런 특성으로 인해서 프로세스와 어플리케이션은 따로 관리됩니다. 프로세스는 운영체제가 관리를 하고, 어플리케이션은 ActivityManagerService가 관리합니다.

그래서 다른 플랫폼에서는 시스템 함수를 호출하기 위해서 매개체를 거칠 필요가 없습니다. 하지만 안드로이드에서는 어플리케이션 정보에 접근하려면 ActivityManagerService를 통해서 접근이 가능합니다. 여기에는 많은 어플리케이션 정보를 key-value형태로 구분을 하는데, 여기서 Context가 등장합니다.

Context는 ActivityManagerService에 접근하기 위한 중간 다리 역할을 합니다. 각 어플리케이션을 대표하는 ID역할을 하게 됩니다.

Context 클래스 구조


위에서 언급한 Context클래스, ContextImpl클래스, ContextWrapper 클래스에 대해서 살펴보겠습니다.


구조는 위의 그림과 같습니다.

Context 추상클래스입니다. 따라서 이를 사용하기 위한 구현체가 있어야 하는데 그것이 ContextImpl입니다. 이 구현체는 사용자에게 노출하지 않고 ContextWrapper로 감싸져 있습니다.

Application, Activity, Service는 ContextWrapper를 상속하여 ContextWrapper의 메서드를 호출하여 Context 구현체(ContextImpl)의 동작을 수행하게 됩니다. 이것은 Proxy패턴으로, 이 패턴으로 Context 구현체와의 결합도를 낮춰 구현체를 쉽게 변경할 수 있다는 장점을 가지고 있습니다.

Context 종류


Application Context

Application Context는 어플리케이션과 관련된 기능을 담고 있으며, 어플리케이션 전체에서 딱 하나만 존재하는 싱글톤 객체입니다. 어플리케이션이 생성될 때 함께 생성됩니다. 따라서 Application의 생명주기와 연관되어 있습니다.

현재 Context가 종료된 후에도 실행될 작업이나, Activity Scope를 벗어난 경우에도 실행되는 작업에 Context가 필요한 경우에 Application Context가 사용됩니다. 또한, 애플리케이션에 싱글턴 오브젝트를 생성하거나 어플리케이션 전체에서 사용할 라이브러리를 초기화하는데 Context가 필요한 하면 Application Context를 사용합니다.

위의 경우에 만약 Activity Context를 사용한다면, 해당 오브젝트가 항상 Activity를 참조합니다. 따라서 Activity가 화면에 표시되지 않은 경우에서 GC가 진행되지 않아서 메모리 누수가 발생합니다.

Application Context는 getApplicationContext()를 통해서 얻을 수 있습니다.

Base Context

  • Activity / Service - ContextWrapper을 상속하여, 자기 자신이 Context입니다. 즉, 액티비티나 서비스가 생성될 때마다 각자의 Context 인스턴스가 생성됩니다. 이 Context는 컴포넌트와 생명주기가 연결되어 있습니다. 따라서 해당 컴포넌트 내에서만 사용이 가능합니다.
  • Broadcast Receiver - 자기 자신의 Context는 아닙니다. 리시버가 브로드캐스트 처리를 할 때마다 Context를 onReceive()의 인자로 전달 받아서 사용합니다. 전달 받은 Context의 생명주기를 따르기 때문에 Activity Context로 브로드캐스트 실행 시 액티비티가 종료되면 브로드캐스트도 함께 종료됩니다.
  • Content Provider - 자기 자신의 Context는 아니다. 동일한 응용 프로그램에 대해 호출 시 동일한 싱글톤 Context 객체를 반환하고, 서로 다른 응용 프로그램에 대해 호출 시 다른 Context 객체를 반환한다.

주의 사항

Application Context는 Activity Context가 제공하는 기능 전체를 제공하는 것은 아닙니다. Dialog같은 GUI(화면, View) 작업은 Application Context로는 불가능합니다. 따라서 해당 작업은 Activity Context를 사용해야 합니다.

이와 다르게 Toast는 Application Context를 사용해도 에러가 나지 않습니다. 이것은 Toast는 Activity와 관련된 윈도우에 속하지 않고 자신만의 윈도우를 생성하기 때문입니다.

Application Context가 사용자 호출로 생성된 clean up되지 않은 객체를 가지고 있다면 메모리 누수가 발생할 수 있습니다. Activity 객체는 가비지 콜렉션이 가능하지만, Application은 프로세스가 살아있는 동안 남아있기 때문입니다.

Context 참조 방법


Activity에서 context

  • this Activity Context를 반환합니다. Activity Scope안에 있으면 this로 가능하고, Activity의 Scope를 벗어나는 경우에서 가져오고 싶다면 this@ActivityName 을 이용해서 가져옵니다.
  • baseContext (getBaseContext()) ContentWrapper의 Context 인스턴스를 반환합니다. 다른 Context를 참조해야되는 경우(?), 그 ContextWrapper 안에 있는 context를 가져와서 사용합니다. 이는 Activity Context의 일종입니다.
  • applicationContext (getApplicationContext()) 어플리케이션 전체적으로 사용되는 곳에서 필요한 Application Context를 가져옵니다.
  • getApplication() Application 인스턴스를 반환합니다. Application도 Context의 자식 클래스이므로 Context처럼 사용할 수 있습니다.

다른 곳에서 Context

  • View의 getContext() View에도 getContext() 메서드가 있어서 Context를 가져올 수 있는데, View를 생성할 때 생성자의 인자로 들어가는 Context가 반환됩니다. 일반적으로는 View가 속해 있는 Activity의 Context가 해당 View의 Context가 됩니다.
  • Fragmentcontext (getContext()) Fragment의 getContext()를 보면 mHost의 Context를 반환합니다. mHost는 Fragment가 부착되어 있는 host이므로 부모 Activity의 Context를 가져오는 것입니다. Context의 종류에는 여러가지가 있지만, 보통 Fragment에서 필요한 Context는 Activity의 Context입니다. (Activity의 Context는 일부 Context보다 덩치가 커서 예전에는 적절한 Context를 쓰는 것이 메모리 관리나 최적화 측면에서 좋았지만 기기 성능이 좋아진 요즘은 괜히 Activity가 아닌 Context를 쓰면 앱이 Crash되는 현상이 생기므로 Activity의 Context를 주로 사용하는 것을 추천한다고 합니다.)

References

https://developer.android.com/reference/android/content/Context

https://blog.naver.com/PostView.nhn?isHttpsRedirect=true&blogId=huewu&logNo=110085457720

profile
안녕하세요

0개의 댓글