Android & Kotlin

[ Android ] Context 와 메모리 릭

쉽코기 2022. 7. 27. 14:28

📌 주요 기능

 

  1. 어플리케이션에 관하여 시스템이 관리하고 있는 정보에 접근
    •  리소스, db, preference 접근 : getResourece() …
    • package 위치 인식 : getPackageName()
  2. 안드로이드 시스템 서비스에서 제공하는 API 호출하기
    • 엑티비티 및 서비스 실행 : startAcitivity(), startService()
    • 시스템 서비스 접근 : getSystemService
      • 안드로이드가 제공하는 시스템-레벨 서비스의 메너저 객체를 반환 받을 수 있다.

 

📌 Context 의 종류와 사용시 주의점 

 

Context 의 종류  

📍 Application Context

  • 싱글턴 인스턴스
  • getApplicationContext 를 통해 접근가능
  • Application 라이프사이클에 의존함

📍 Base Context

  • Activity 나 Service 내에서 유효한 context 이다.
  • Activity 에 대응하는 context 를 Activity Context 로 칭한다.
  • Activity, Service 라이프 사이클을 따른다.

 

사용시 주의점 : Memory Leak

싱글턴 오브젝트를 생성하고 이때 컨텍스트가 필요하다면 AcitivityContext가 아닌 ApplicationContext 를 주입해야합니다. Acitivity Context 를 주입할 경우 해당 Acitiviity가 destory 되더라도 싱글턴 오브젝트에서 사용하고 있으므로 ActivityContext 가 Garbage Collection 이 진행되지 않아 memory leak 이 발생합니다.

 

또한 ViewModel 에서 ActivityContext 를 넘겨받는 잘못된 경우를 생각해볼 수 있습니다. ViewModel 은 Activity 보다 긴 생명주기를 갖게되고 Activity가 Destory 상태에 도달하더라도 ViewModel 의 참조로 인해서 ActivityContext 는 GC 에 의해 메모리 해제되지 않아 메모리 릭이 발생하게 됩니다

 

결론적으로 안드로이드에서는 생명주기가 짧은 쪽에서 긴쪽의 사용하는 것이 메모리 릭을 막을 수 있는 방법입니다.

그렇다면 모든 상황에서 ActivityContext 보다 생명주기가 긴 ApplicationContext 를 사용하면 안전할까요?

그렇지 않습니다. Dialog, Toast 등 화면 관련된 부분은 Activity Context 를 사용해야합니다.

 

메모리 릭에 대해서는 아래 두 가지 툴을 통해 진행할 수 있습니다. 

 

https://github.com/square/leakcanary

 

GitHub - square/leakcanary: A memory leak detection library for Android.

A memory leak detection library for Android. Contribute to square/leakcanary development by creating an account on GitHub.

github.com

 

https://developer.android.com/studio/profile/memory-profiler?hl=ko 

 

메모리 프로파일러를 사용하여 앱의 메모리 사용량 검사  |  Android 개발자  |  Android Developers

끊김 현상, 멈춤, 심지어 비정상 종료를 일으킬 수 있는 메모리 누수 및 메모리 변동을 식별하는 데 도움이 되는 Android 프로파일러의 메모리 프로파일러 구성요소를 알아보세요.

developer.android.com


 Context  심층 탐구

📌 Context 의 구성

 

Application, Activity, Service 는 Context 를 상속하고 있으며 위에 포함되지 않은 4대 Component 들인 ContentProvider 와 BroadcastReceiver 또한 context 를 사용합니다.

 

또한 Context 튼 ConextWrapper 는 내부적으로 Context 객체를 들고 있고 모든 동작을 Context 구현체의 함수로 위임하게 됩니다.(decorate 디자인패턴)

위 와같은 구현의 장점은 내부 구현체(객체)가 바뀐다 하더라도 Context 를 사용하는 방식(사용 메서드) 는 그대로 유지할 수 있습니다. 또한 잘못된 방식으로의 Context 의 사용이나 변경을 막을 수도 있습니다.

 

 

📌 왜 Context 필요할까?

context 가 필요한 근본적인 이유에 대해서 생각해보았습니다.

 

위에 말한 2가지 기능에서 Application 전역적인 정보에 대한 바로 접근하던 시스템 서비스의 api 를 사용하던 안드로이드 시스템에 직접 접근하면 될텐데 왜 중간에 Context 라는 개념이 존재할까라는 context 의 존재에 대한 의문을 갖게 되었습니다.

(예: 접근시에는 System.getPackageName() 으로 구성하면 편할 텐데 왜this.getApplicationContext.getPackageName() 으로 접근해야함)

 

 

그 이유는 안드로이드 플렛폼에선 어플리케이션과 프로세스가 별도로 관리되어지며 각각의 어플리케이션은 AcitivityManagerService 라는 일종에 다른 어플리케이션을 통해 관리 되어 지기 때문입니다.

AcitivityManagerService 에서는 어플리케이션을 특정 토큰을 키값으로 ‘key-value’ 쌍으로 관리하게 되고 이에 따라 자신이 어떤 어플리케션인지를 알려주는 즉 key 를 담는 객체가 필요합니다. 여러 앱을 관리하기에 어떤 앱으로 부터의 요청인지 알기 위해서 입니다. 이러한 AcitivityManagerServcie 로의 접근을 가능하게 하는 통로 역할을 수행하는 것이 바로 context 라고 할 수 있습니다.

 

마지막으로 정리하면 Android OS 는 어플리케이션과 프로세스가 1:1 대응하는 구조가 아닙니다.

중간에 ActivityManagerService 를 통해 여러 앱이 관리되어진다.

이에따라 어플리케이션 정보를 얻기 위해서는 자신이 어떤 앱인지 ActivityManagerService에게 알려줄 수 있는 context 가 필요하다