티스토리 뷰
안드로이드에서 Context는 가장 많이 등장하고, 그만큼 핵심이 되는 개념이기 때문에 제대로 알고 사용하는 것이 좋다. 이번 포스팅에서는 Context의 역할 및 종류와 언제 사용하면 좋은지 알아 보려고 한다.
Context란?
Context는 어플리케이션 환경에 관한 전체 정보를 받을 수 있는 추상 클래스로 액티비티, 브로드캐스트, 서비스를 시작할 때도 사용되고 리소스에 접근할 때도 Context가 사용된다. 이처럼 어플리케이션과 관련된 정보에 접근하고자 하거나 어플리케이션과 연관된 시스템 레벨의 함수를 호출하고자 할 때 Context가 필요하다.
안드로이드는 어플리케이션 관련 정보를 ActivityManagerService에서 관리하고 있고, 어플리케이션과 관련된 정보에 접근하려고 할 때 해당 어플리케이션에 대한 식별자가 필요한데 그 역할을 하는 것도 Context이다.
ContextWrapper와 ContextImpl
Context는 추상 클래스이기 때문에 이를 사용하기 위해서는 구현체가 있어야 한다. 기본 구현체는 ContextImpl이고, 이 구현체는 사용자에게 노출하지 않고 ContextWrapper로 감싸져 있다.
[참고 사항]
- ContextImpl 클래스는 앱에서 직접 사용할 수 있는 클래스가 아니기 때문에 소스코드로만 확인할 수 있다.
- 앞으로 "Context 구현체"라는 용어가 등장하는데, 이는 ContextWrapper가 wrapping하는 Context 구현체가 ContextImpl이 아닐 수 있기 때문에 ContextImpl과 용어를 구분하기 위함이다.
Context의 클래스 구조는 아래 그림과 같다.
ContextWrapper 클래스는 생성자로 Context 구현체를 받아서 Context 동작을 Context 구현체의 동작으로 위임한다. 즉, ContextWrapper를 상속하는 Activity, Service, Application과 같은 컴포넌트에서 ContextWrapper의 함수를 호출하면 Context 구현체의 동작을 수행하게 하는 것이다. 이를 Proxy 패턴이라고 하는데, 이 패턴을 사용하면 Context 구현체와 이를 사용하는 쪽의 결합도가 낮아져 쉽게 Context 구현체를 변경할 수 있다는 장점이 있다. 이를 공식 문서에서는 원본 Context의 변경 없이 Context 동작을 수정할 수 있다고 설명하고 있다.
더 자세한 이해를 위해 ContextWrapper 클래스의 코드를 보면 아래와 같다.
코드에서 볼 수 있듯이 ContextWrapper는 생성자로 Context의 구현체를 받는다. 그리고 Context의 모든 동작을 Context 구현체의 함수로 위임한다.
이 구조가 좋은 점은 앞서 언급한 것 처럼 Context 구현체를 바꾸어도 Context를 사용하던 코드는 변경하지 않아도 된다는 것이다. ContextWrapper에서 Context 구현체를 바꾸는 메서드가 attachBaseContext(Context base)이다.
Context의 종류
위의 Context 구조에서 볼 수 있듯이 ContextWrapper를 상속하는 컴포넌트는 Activity, Service, Application이다. Broadcast Reciever나 Content Provider는 ContextWrapper를 상속하지 않는다.
여기서 Acitvity, Service, Application 컴포넌트는 각각 ContextImpl을 생성하고 ContextWrapper에서 getBaseContext()와 getApplicationContext() 메서드를 사용해 Context를 반환한다.
-
Application Context
- 싱글톤 객체로 어플리케이션의 생명주기를 따른다. 즉, 앱의 시작 시 부터 종료 시 까지 살아있다.
-
Activity Context
- Activity 자체가 Context를 상속하고 있기 때문에 Activity 인스턴스 자체가 Context 역할을 한다.
- 그렇기 때문에 Activity Context는 액티비티 생명주기를 따른다.
Context Type
-
Application - 싱글톤으로 어플리케이션 객체가 생성될 때 함께 Application Context가 생성되기 때문에 Application Context는 동일한 앱 안에서 항상 동일한 인스턴스를 반환한다.
-
Activity / Service - 액티비티나 서비스가 생성될 때마다 각자의 Context 인스턴스가 생성된다.
-
Broadcast Receiver - 자기 자신이 Context는 아니다. 리시버가 브로드캐스트 처리를 할 때마다 Context를 onReceive()의 인자로 전달 받아서 사용한다. 전달 받은 Context의 생명주기를 따르기 때문에 액티비티 컨텍스트로 브로드캐스트 실행 시 액티비티가 종료되면 브로드캐스트도 함께 종료된다.
-
Content Provider - 자기 자신이 Context는 아니다. 동일한 응용 프로그램에 대해 호출 시 동일한 싱글톤 Context 객체를 반환하고, 서로 다른 응용 프로그램에 대해 호출 시 다른Context 객체를 반환한다.
Context 참조 방법
ActivityName.this
- Activity Context를 반환한다. Activity Scope 안에서 사용할 때는 this만으로도 가져올 수 있다.
Java에서는 Activity의 Inner Class와 같이 Activity Scope를 벗어난 곳에서 액티비티 인스턴스를 참조하고 싶을 때 ActivityName.this로 가져올 수 있다.
마찬가지로 Kotlin에서는 higher order function을 많이 사용하기 때문에 Activity의 Scope를 벗어나는 경우가 자주 있는데 해당 Scope에서 액티비티 인스턴스를 가져오고 싶을 때 this@ActivityName을 이용해 가져올 수 있다.
getApplicationContext()
- Application Context를 반환한다.
getApplication()
- Application 인스턴스를 반환한다. Application도 Context의 자식 클래스이므로 Context처럼 사용할 수 있다.
- 하지만 Application과 Application Context가 항상 동일한 인스턴스라는 보장이 없고, Application은 Activity나 Service 내부에서만 참조가 가능하다는 차이점도 있다.
getBaseContext()
- ContextWrapper의 Context 인스턴스를 반환한다. 다른 Context를 참조해야 하는 경우, 그 ContextWrapper 안에 있는 context를 getBaseContext()로 가져와 사용한다. 이는 Activity Context의 일종이다.
View의 getContext()
- View에도 getContext() 메서드가 있어 Context를 가져올 수 있는데, View를 생성할 때 생성자의 인자로 들어가는 Context가 getContext()의 결과로 반환된다. 일반적으로 View가 속해 있는 Activity의 Context가 해당 View의 Context가 된다.
Context의 생명주기가 다르기 때문에 Activity와 분리된 작업에 Activity Context를 사용하면 예상치 못한 동작이 발생할 수 있다. 반대로 Activity에 종속된 작업을 Application Context를 이용하면 Activity가 종료되어도 Context를 이용한 작업이 종료되지 않아 memory leak이 발생할 수도 있다. 그렇기 때문에 Context를 사용할 때 각 Context의 특징과 Context가 필요한 작업의 특성을 고려해 Context를 알맞게 선택해 사용해야 한다.
[참고한 사이트]
'Mobile > Android' 카테고리의 다른 글
[Android] ExoPlayer blink(깜빡임 현상) 제거하기 (0) | 2021.05.19 |
---|---|
[Android] ListAdapter로 리스트 효율적으로 관리하기 (with DiffUtil) (2) | 2020.06.12 |
[Android] RecyclerView의 데이터 LiveData로 관리하기 (0) | 2020.05.26 |
[Android] Retrofit2 + Gson 사용하기 (with Kotlin) (0) | 2020.05.07 |
[Android] Properties 값 Java/xml 파일에서 읽어오기 (0) | 2020.04.08 |
- Total
- Today
- Yesterday
- SQL Server
- Android
- AsyncListDiffer
- 내용제공자
- 안드로이드
- SOCKET
- covariance
- python3
- RuntimeException
- personal access token
- Java
- SQL
- kotlin
- Algorithm
- gson
- ViewHolder
- 알고리즘
- 부스트코스
- 파이썬
- MSSQL
- 위험권한
- 프로그래머스
- RecyclerView
- pecs
- DiffUtil
- Python
- GitHub
- SQLD
- SQLiteOpenHelper
- AndroidStudio
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |