앱 개발을 하다보면, Context는 필요하다. 이가 없으면 액티비티를 시작할 수도, 브로드캐스트를 발생시킬 수도, 서비스를 시작할 수도 없다. 리소스에 접근할 때도 Context를 통해서만 가능하다.
Context는 여러 컴포넌트의 상위 클래스이면서 Context를 통해서 여러 컴포넌트가 연결돼 있으므로 Context 자체를 살펴보는 것이 컴포넌트를 이해하는 데도 도움이 된다.
이 클래스 하위 클래스로는 ContextWrapper가 있고 이를 상속하는 아래 3가지가 있다.
Activity, Service, Application을 보면 ContextImpl을 직접 상속하지 않고, ContextImpl 메서드를 호출하는 형태를 갖고 있다.
이렇게 하면 ContextImpl 변수가 노출되지 않고, ContextWrapper에서는 ContextImpl의 공개 메서드만 호출하게 된다.
또한, 각 컴포넌트별로 사용하는 기능을 제어하기도 단순해진다.
ex) Activity
3개의 인스턴스가 다르기 때문에 함부로 캐스팅하면 안된다.
getBaseContext()
로 가져온 걸 Activity로 캐스팅하면 ClassCastException이 발생한다.
cf) https://velog.io/@woga1999/Android%EC%9D%98-Context
Context와 컴포넌트 중간에 있는 ContextWrapper는 그 이름처럼 Context를 래핑한 ContextWrapper(Context base)
생성자를 갖고 있다.
ContextWrapper.java
@UnsupportedAppUsage
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
base 파라미터에 전달되는 것은 Context의 여러 메서드를 직접 구현한 ContextImpl 인스턴스이다.
@Override
public Context getApplicationContext() {
return mBase.getApplicationContext();
}
ContextWrapper의 여러 메서드는 base의 메서드를 그대로 호출한다.
Activity, Service, Application은 public ContextWrapper(Context base) { mBase = base; }
이 생성자를 사용하지 않고, 실제로는 attachBaseContext()
메서드를 사용한다.
Activity, Service, Application 모두 내부적으로 ActivityThread에서 컴포넌트가 시작된다. 이때 각 컴포넌트의 attach()
메서드를 실행하고 attach() 메서드에서 다시 attachBaseContext()
메서드를 호출한다.
ContextWrapper 생성자에 전달되는 ContextImpl은 앱에서 싱글톤으로 단 1개의 인스턴스만 있는가? => 그렇지 않다
ContextWrapper에 getBaseContext()
와 getApplicationContext()
라는 2개의 별도 메서드를 보고 유추할 수 있따.
Acitivity, Service, Application 컴포넌트는 각각 생성한 ContextImpl을 하나씩 래핑하고 있고 getBaseContext()는 각각 ContextImpl 인스턴스를 리턴한다.
getApplicationContext()
는 Application 인스턴스를 리턴하는 것이므로 앱에서 1개 밖에 없는 어디서나 동일한 인스턴스를 반환한다.
Helper, Permission, system_service 이렇게 3개 그룹으로 나눌 수 있다.
ActivityManagerService
의 메서드 다시 호출