[개념] Context

쓰리원·2022년 4월 27일
0
post-thumbnail

개발하면서 Context를 자주 보지만, 정확하게 이해하지 않고 사용하고 있었습니다. 그러던 와중 공식 문서를 봤는데, 내용이 너무 방대해서 다른 글들을 참조하여 정리를 하려고 합니다. 틀린 내용이 있을 수 있으니 감안하고 보시면 좋겠습니다.

1. Context 란?

공식문서를 보면 Context에 대한 설명으로 위 글이 있는 것을 볼 수 있습니다. 해석을 하자면, Application 환경에 대한 전역 정보에 대한 인터페이스입니다. 이것은 Android 시스템에서 구현을 제공하는 추상 클래스입니다. 이를 통해 애플리케이션별 리소스 및 클래스에 대한 액세스는 물론 launching activities, broadcasting and receiving intents 등과 같은 애플리케이션 수준 작업에 대한 호출을 허용합니다. 라고 합니다.

1. Context 개념

  • Application 환경에 대한 전역 정보를 접근하기 위한 인터페이스.
  • 추상 클래스이며 실제 구현은 Android 시스템에 의해 제공된다.

2. Context 역할

  • Context를 통해 어플리케이션 시스템이 관리하고 있는 리소스나 클래스에 접근할 수 있다. ex) getPackageName(), getResource()
  • Activity 실행, Intent 브로드캐스팅 그리고 Intent 수신 등과 같은 응용 프로그램 수준의 작업을 수행하기 위한 API를 호출 할 수 있다.
    ex) startActivity() 나 bindService()

2. Context 구조

1. ContextThemeWrapper 와 ContextWrapper

Context는 ContextThemeWrapper 와 ContextWrapper로 구분할 수 있습니다. Activity는 ContextThemeWrapper를 상속 합니다. 그래서 layout xml을 inflate 할 때 view에 theme가 적용되게 됩니다.

만약 ContextWrapper로 inflate하면 theme가 적용되지 않습니다. Application context, Broadcast, Service에 전달되는 context는 ContextWrapper 입니다. ContextWrapper는 application context를 제외하면 모두 수명이 짧습니다.

UI 관련된 것에 접근하려면 ContextThemeWrapper를 사용하면 됩니다. 그 외에는 ContextWrapper를 사용합니다. short-living context를 long-living object 에 전달하지 말아야 합니다. memory leak이 생길 수 있습니다.

2. ContextWrapper & ContextImpl

Context는 추상 클래스이기 때문에 이를 사용하기 위해서는 구현체가 있어야 합니다. 기본 구현체는 ContextImpl이고, 이 구현체는 사용자에게 노출하지 않고 ContextWrapper로 감싸져 있습니다.

ContextImpl 클래스는 앱에서 직접 사용할 수 있는 클래스가 아니기에 소스코드로만 확인이 가능합니다. 앞으로 Context의 구현체라는 말은 ContextWrapper가 wrapping하는 Context 구현체가 ContextImpl이 아닐 수 있기에 ContextImpl과의 용어 구분을 위해 Context 구현체라고 합니다.

ContextWrapper 클래스는 생성자로 context 구현체를 받아서 context동작을 context 구현체의 동작으로 위임합니다.

즉, ContextWrapper를 상속하는 Activity, Service, Application과 같은 컴포넌트에서 ContextWrapper의 함수를 호출하면 Context 구현체의 동작을 수행하게 합니다.

ContextWrapper가 Context의 구현체의 동작을 수행합니다. 이를 Proxy 패턴이라고 하는데, 이 패턴을 사용하면 Context 구현체와 이를 사용하는 쪽의 결합도가 낮아져 쉽게 Context 구현체를 변경할 수 있다는 장점이 있습니다. ContextWrapper로 구현가능한 건 아래와 같이 ContextWrapper 클래스를 열어보면 알 수 있습니다.

그래서 ContextWrapper는 생성자로 Context 구현체를 받고, Context의 모든 동작을 Context 구현체의 함수로 위임을 합니다. 이 구조가 좋은 점은 앞서 말했듯 Context 구현체를 바꾸어도 Context를 사용하던 코드는 변경되지 않아도 됩니다. ContextWrapper에서 Context구현체를 바꾸는 메서드는 attachBaseContext(Context base) 입니다.

3. Context의 종류.

위의 Context 구조에서 볼 수 있듯이 ContextWrapper를 상속하는 컴포넌트는 Activity, Service, Application 입니다. BroadcastReceiver나 ContentProvider는 ContextWrapper를 상속하지 않습니다. 여기서 Activity, Service, Application 컴포넌트는 각각 ContextImpl을 생성하고 ContextWrapper에서 getBaseContext()와 getApplicationContext() 메서드를 호출하여 Context를 반환합니다.

  • Application Context : Singleton 객체로 어플리케이션의 생명주기를 따릅니다. 앱의 시작부터 종료시까지 살아있습니다.
  • Activity Context : Activity 자체가 Context를 상속하고 있기에 Activity 인스턴스 자체가 Context 역할을 합니다. 그렇기에 Activity Context는 액티비티 생명주기를 따릅니다.

4. Context의 사용.

전역적인 어플리케이션 정보에 접근하거나 어플리케이션 연관된 시스템 기능을 수행하기 위해, 시스템 함수를 호출하는 직접적으로 시스템 API 호출하면 됩니다. 반면 안드로이드에서는 Context 라는 인스턴스화된 매개체를 통해야만 유사한 일들을 수행할 수 있습니다.

예를 들어, C# 에서 어플리케이션 이름을 가져오고, 다른 어플리케이션을 실행시키는 코드는 아래와 같이 작성될 수 있습니다.

 //Get an Application Name.
 String applicationName = System.AppDomain.CurrentDomain.FriendlyName;

 //Start a new process(application)
 System.Diagnostics.Process.Start("test.exe");

반면에 안드로이드 Activity 에서는 아래와 같이 작성 해야 됩니다.

//Get an application name
String applicationName = this.getPackageName();

//Start a new activity(application)
this.startActivity(new Intent(this, Test.class));

C#의 경우 System단에서 제공하는 정적 함수(static function)를 호출 함으로서 간단하게 할 수 있는 일들을 안드로이드에서는 Context에 정의된 인스턴스 함수를 호출해야만 가능하게 되어있습니다. 즉, 위에서 처럼 반드시 인스턴스화된 Context 클래스(여기서는 this)를 사용해야 합니다.

안드로이드에서 어플리케이션과 프로세스는 서로 독립적으로 존재입니다. 예를 들자면, 안드로이드 플랫폼에서는 프로세스가 없는 상황에도 어플리케이션은 살아있는 것처럼 사용자에게 표시되기도 하고, 메모리가 부족한 상황이 될 경우, 작동중이던 프로세스가 강제로 종료되고, 대시 해당 프로세스에서 작동중이던 어플리케이션에 관한 일부 정보만 별도로 관리하고, 이 후에 메모리 공간이 확보되면 저장되어있던 어플리케이션 정보를 바탕으로 새로운 프로세스를 시작하는 등의 일이 발생합니다.

안드로이드에서도 프로세스는 OS 커널 (리눅스)에서 관리됩니다. 어플리케이션과 프로세스가 별도로 관리되고 있다면, 어플리케이션 정보는 안드로이드의 시스템 서비스 중 하나인 ActivityManagerService 에서 그 책임을 집니다. 그렇다면 ActivityManagerService 는 특정 토큰을 키값으로 'Key-Value' 쌍으로 이루어진 배열을 이용해 현재 작동중인 어플리케이션 정보를 관리합니다.

Context 는 어플리케이션과 관련된 정보에 접근하고자 하거나 어플리케이션과 연관된 시스템 레벨의 함수를 호출하고자 할 때 사용됩니다. 그런데 안드로이드 시스템에서 어플리케이션 정보를 관리하고 있는 것은 시스템이 아닌, ActivityManagerService 라는 일종의 또 다른 어플리케이션입니다. 따라서 다른 일반적은 플랫폼과는 달리, 안드로이드에서는 어플리케이션과 관련된 정보에 접근하고자 할때는 ActivityManagerService 를 통해야만 합니다. 당연히 정보를 얻고자 하는 어플리케이션이 어떤 어플리케이션인지에 관한 키 값도 필요해집니다.

일반 OS 플랫폼에서 어플리케이션은 곧 Process 입니다. 특정 어플리케이션이 OS 에게 내가 어떤 Process 인지만 알려주면 어플리케이션 관련된 정보를 얼마든지 획득 할 수 있습니다. 이른바 자신의 존재 자체가 자신임을 증명해주는 '지문인식' 혹은 '홍채인식' 등의 '생체인식' 과 비슷한 개념이기 떄문에 Context 와 같은 애매한 중간 매개체가 존재할 이유가 없습니다.

하지만 안드로이드 플랫폼은 조금 다릅니다. 비유하자면 '생체인식' 보다는 'ID카드' 를 통한 보안 시스템과 유사한 구조입니다. 특정 어플리케이션이 자신이 본인임을 확인 받을 수 있는 방법은 자신이 작동중인 Process 를 보여주는 것이 아니라, 자신이 건내받은 ID카드를 제시하는 것 입니다. 이 때 ID카드의 역할을 수행하는 것이 바로 Context 이고, 당연히 이 카드는 위변조가 가능하기때문에, 자신의 권한을 제삼의 어플리케이션에게 넘겨주는 PendingIntent 와 같은 기능도 가능해집니다.

5. Application과 Context의 관계

Context 클래스는 추상 클래스로 되어있고, 애플리케이션(Application), 액티비티(Activity), 서비스(Service)는 이 Context 추상 클래스를 상속받아서 Context의 기능들을 이용합니다

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

6. Mainly two types of context

1. Application Context

Application Context는 애플리케이션과 관련되어 있고 애플리케이션이 실행되어있는 동안 변경되지 않습니다. 그러므로 싱글톤이며 getApplicationContext()를 통해 Activity에서 접근할 수 있는 인스턴스 입니다. 이 Context는 애플리케이션의 생명주기와 관련되어있기 때문에 현재 Context와 분리된 어떤 Context가 필요하거나, 현재 Activity Scope을 벗어난 작업을 할 때 필요합니다.

어떤 싱글톤 객체를 만들어야하고 해당 객체에 항상 Context가 필요한 경우에 Application Context를 전달하면 됩니다.

Application Context에 Activity를 참조하게 되면 Activity가 부서지더라도 참조가 유지되므로, Activity가 GC(가비지 콜렉팅)되지 않으므로 메모리 누수가 발생합니다. 다른 Context보다 가장 오래 보존될만한 Context가 필요할때만 getApplicationContext()를 사용해야 합니다.

2. Activity Context

Activity Context는 액티비티와 관련이 있고, 액티비티가 부서지면 컨텍스트도 같이 부서집니다. 하나의 애플리케이션에서 여러 액티비티가 존재 할 수 있는데 때로는 특정 Activity Context를 다룰일이 생깁니다. 예를 들어 새로운 액티비티를 실행한다면, 액티비티(컨텍스트)가 필요합니다. 새로 띄워진 액티비티는 이전 액티비티와 연관되어진체로 액티비티 스택에 보관됩니다. Application Context를 이용하여 새로운 액티비티를 띄울수도 있지만 그럴경우 반드시 Intent.FLAG_ACTIVITY_NEW_TASK 플래그를 설정 해주어야합니다.

x. reference

https://developer.android.com/reference/kotlin/android/content/Context
https://medium.com/free-code-camp/mastering-android-context-7055c8478a22
https://blog.mindorks.com/understanding-context-in-android-application-330913e32514
https://stackoverflow.com/questions/10347184/difference-and-when-to-use-getapplication-getapplicationcontext-getbasecon
https://www.charlezz.com/?p=1080
https://www.notion.so/Context-217aa4528b704e799ea2d936a2fbe800
https://velog.io/@jaeyunn_15/Android-Context
https://velog.io/@sery270/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-Context
https://blog.naver.com/PostView.naver?blogId=huewu&logNo=110085457720&parentCategoryNo=&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView

profile
가장 아름다운 정답은 서로의 협업안에 있다.

0개의 댓글