Tech & Programming/모바일(Android, Flutter)

Android Context

소스코드 요리사 2020. 11. 13. 12:41

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.

번역을 해보자면, 어플리케이션 환경에 대한 글로벌 정보에 대한 인터페이스입니다.

Android 시스템에서 구현을 제공하는 추상 클래스입니다. 이를 통해 애플리케이션 별 리소스 및 클래스에 액세스 할 수 있을뿐만 아니라 Activity들의 시작, 인 텐트, 브로드캐스트 수신 등과 같은 애플리케이션 수준 작업에 대한 상향 호출도 가능합니다.

 

  즉, Context 이용해 getPackageName(), getResource(), StartActivity(), startService(), getSystemService() 같이 시스템 레벨의 정보를 얻을 수 있으며, 리소스확인, 데이터베이스 및 환경설정에 대한 엑세스 확인이 가능합니다. 대표적으로 상속 받고 있는 것이 Activity 입니다.

 

아래는 대표적으로 Context 상속하고 있는 subclass 입니다. 자세한 것은 공식 문서를 참고

java.lang.Object

   ↳ android.content.Context

 

[ 출처  :  찰스의   안드로이드   블로그 ]

 

 

 

  Context 중요한 것은 안드로이드의 중요한 컴포넌트들이 상속으로 사용을 하고 있기 때문에 참조를 저장하거나 하면 컴포넌트들이 생명주기에 의해 파괴되어도 참조되어 있는 Context 때문에 메모리를 누수가 생기는 결과를 초래할 있기 때문입니다.

 

Application Context?

Context 내부에 보면 'public abstract Context getApplicationContext ()' 메소드가 있습니다.

메소드를 호출하면 ApplicationContext 리턴되는데, 아무 생각없이 자주 호출할 때가 많은데 이 메소드를 통해 ApplicationContext가 뭔지 알아봅시다.

 

getApplicationContext 메소드에 대한 공식 문서의 내용

Return the context of the single, global Application object of the current process. This generally should only be used if you need a Context whose lifecycle is separate from the current context, that is tied to the lifetime of the process rather than the current component.

현재 프로세스의 단일 전역 Application 개체의 context 를 반환합니다.

더보기

{참고] Application

public class Application

 

extends ContextWrapper implements ComponentCallbacks2

   ↳ android.content.Context

        ↳ android.content.ContextWrapper

               ↳ android.app.Application    

 

Known direct subclasses

MockApplication

 

Base class for maintaining global application state. You can provide your own implementation by creating a subclass and specifying the fully-qualified name of this subclass as the "android:name" attribute in your AndroidManifest.xml's <application> tag. The Application class, or your subclass of the Application class, is instantiated before any other class when the process for your application/package is created.

 

  글로벌 애플리케이션 상태를 유지하기위한 기본 클래스입니다. 하위 클래스를 만들고이 하위 클래스의 정규화 된 이름을 AndroidManifest.xml <application> 태그에 "android : name"속성으로 지정하여 자체 구현을 제공 할 수 있습니다. Application 클래스 또는 Application 클래스의 하위 클래스는 애플리케이션 / 패키지에 대한 프로세스가 생성 될 때 다른 클래스보다 먼저 인스턴스화됩니다.

 

  일반적으로 Application을 하위 클래스화할 필요가 없습니다. 대부분의 상황에서 정적 싱글 톤은 더 모듈화 된 방식으로 동일한 기능을 제공 할 수 있습니다. 싱글 톤에 전역 컨텍스트가 필요한 경우 ( : 브로드 캐스트 수신기 등록), 싱글 톤의 getInstance () 메서드를 호출 할 때 Context.getApplicationContext () Context 인수로 포함합니다.

  이것은 일반적으로 현재 구성 요소가 아닌 프로세스의 수명에 연결된 현재 컨텍스트와 수명주기가 분리 된 컨텍스트가 필요한 경우에만 사용해야합니다

 

  여기서 주의 깊게 것은 Application 개체의 Context를 반환한다는 것, 현재 구성 요소가 아닌 프로세스의 수명입니다. , 싱글턴의 Application 개체의 Context이며, 프로세스가 끝나야 이 AcpplicationContext 가 파괴된다는 뜻이다. 이 Context는 애플리케이션의 생명주기와 관련되어있기 때문에 현재 Context와 분리된 어떤 Context가 필요하거나, 현재 Activity Scope을 벗어난 작업을 할때 필요합니다.

 

  관련된 예제로 공식 문서는 registerReceiver 들고 있습니다.

Consider for example how this interacts with registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter):

번역 : registerReceiver (android.content.BroadcastReceiver, android.content.IntentFilter)와 상호 작용하는 방법을 고려하세요.

 

If used from an Activity context, the receiver is being registered within that activity. This means that you are expected to unregister before the activity is done being destroyed; in fact if you do not do so, the framework will clean up your leaked registration as it removes the activity and log an error. Thus, if you use the Activity context to register a receiver that is static (global to the process, not associated with an Activity instance) then that registration will be removed on you at whatever point the activity you used is destroyed.

번역 :  Activity context 로부터 사용되는 경우 receiver Activity 내에 등록이 된다. 이것은 Activity destroyed 되기 전에 등록된 것을 제거해야하는 것을 의미한다. 실제로 그렇게 하지 않으면 프레임 워크는 Activity 제거하고, receiver 정리하고 error 로그를 기록한다. 따라서 만약 너의 Activity context static receiver(Activity 인스턴스와 연관 없는 global process 연관이 있는) 등록한다면 activity 소멸 해당 receiver 제거 있다.

 

 

If used from the Context returned here, the receiver is being registered with the global state associated with your application. Thus it will never be unregistered for you. This is necessary if the receiver is associated with static data, not a particular component. However using the ApplicationContext elsewhere can easily lead to serious leaks if you forget to unregister, unbind, etc.

번역 : 이 메소드에서 리턴되는 Context 사용하면, 어플리케이션 global state 관련된 전역상태에 등록 된다. 따라서, (Actitivy 되어도) receiver 취소되지 않는다. Receiver 특정 컴포넌트와 관련이 있는 것이 아니라 정적 데이터와 관련이 있을 이런 사용이 필요하다그러나, 다른 곳에서 ApplicationContext  을 사용하는 것은 unregister, unbind 등의 자원해제를 잊을 경우 심각한 누수가 발생할 수 있다.  쉽게 말해 정적 메소드나 싱글턴 인스턴스와 같이 프로그램과 주기를 같이 하는 곳에 ApplicationContext 아닌 ActivityContext 참조하고 있다면 Activity Destoryed 되어도 참조가 있기 때문에 메모리 릭이 발생하게 된다.

 

getBaseContext()?

ContextWrapper class 존재하는 메소드입니다.

더보기

[참고] ContextWrapper

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

 

Proxying implementation of Context that simply delegates all of its calls to another Context. Can be subclassed to modify behavior without changing the original Context.

단순히 모든 호출을 다른 Context에 위임하는 Context 의 프록시 구현입니다. 원래 Context 를 변경하지 않고 동작을 수정하기 위해 서브 클래싱 할 수 있습니다.

 

실제 전달되는 구현체는 ContextImpl 이며, 앱에서 직접 사용할 있는 클래스가 아닙니다.

ContextWrapper에는 Context의 여러 메소드를 구현한 ContextImpl 인스턴스를 전달 받습니다.

ContextWrapper 는 ContextImpl(http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/app/ContextImpl.java#120) 에서 구현한 함수를 중간에서 일부만 공개해 주거나 수정을 합니다. (디자인 패턴 중 보호 프록시역활을 합니다.)

그렇게 함으로서 컴포넌트(Acitvity, Service, Application)들이 ContextImpl을 직접 상속해서 Context 메소드를 사용하는게 아니라 ContextWrapper 을 중간에 두어. Context 메소드를 사용하게 합니다. 이는"상속보다는 구성을 사용하라" 는 객체 지향의 원칙을 준수하기 위함입니다. 그리고, attachBaseContext 메소드의 역할은 ContextWrapper가 context를 오직 한번만 적용되도록 한다.

공식문서에는 the base context as set by the constructor or setBaseContext 라고 나와 있습니다.

, setBaseContext 또는 생성자에 의해 set BaseContext 리턴된다. 다른 Context 로부터 어떤 Context 접근해야하는 ContextWrapper 사용합니다. ContextWrappper 내부에서 참조된 Context getBaseContext() 통해 엑세스 됩니다.

그리고, getBaseContext() 받은 context Acivity Cast 하면 ClassCaseException 발생합니다.

(ApplicationContext 또한 Activity 캐스팅하면 Exceeption 발생)

 

 

ActivityContext?

View.getContext() 를 호출하게 되면 VIew 가지고 있는 Context 리턴하는데, 일반적으로 view Activity 위에 표시되기 때문에 ActivityContext 리턴이 됩니다. 또는 Activity 에서 getContext 하면 리턴되는 Context 바로 ActivityContext입니다.

이 Context는 Activity Destory 되면 같이 Destroy 입니다.

하나의 애플리케이션에서 여러 Activity가 존재 할 수 있는데 때로는 특정 Activity Context를 다룰일이 생깁니다.

예를 들어 새로운 Activity를 실행한다면, Activity(컨텍스트)가 필요합니다.

새로 띄워진 Activity는 이전 Activity와 연관되어진체로 Activity 스택에 보관됩니다.  Application Context를 이용하여 새로운 액티비티를 띄울수도 있지만 그럴경우 반드시 Intent.FLAG_ACTIVITY_NEW_TASK 플래그를 설정 해주어야 합니다. 

.

따라서, 정적 메소드나 싱글턴 인스턴스에 ApplicationContext 아닌 ActivityContext 참조하고 있다면 Activity Destoryed 되어도 참조가 있기 때문에 메모리 릭이 발생하게 됩니.

 

레퍼런스

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

https://black-jin0427.tistory.com/220

https://c10106.tistory.com/4703

https://www.charlezz.com/?p=1080

https://shinjekim.github.io/android/2019/11/01/Android-context%EB%9E%80/

https://blog.mindorks.com/understanding-context-in-android-application-330913e32514