Android Context, What Context?

안드로이드의 Context는 어플리케이션에서 가장 많이 사용되는 요소이다. 많이 사용되는 만큼 잘못 사용하는 경우도 흔하다..

Context 오브젝트는 너무 광범위하고 자주 사용하다 보니 의도하지 않은 상황이 쉽게 만들어 질 가능성이 매우 높다. 리소스를 로드한다던가, 시스템서비스 상태를 얻는다던가, 새로운 뷰를 생성하는 등 Context는 상황에 맞는 작업을 수행 할 것이다. 이렇게 많이 쓰이다 보니 오용될 여지가 있다. 그래서 이번 글을 통해 어떻게 하면 오용되지 않고 어플리케이션에서 보다 효과적으로 활용 할 수 있도록 몇가지 팁을 써볼까 한다.

Context 타입

새로운 instances생성시 모두 같은 Context를 필요로 하지는 않는다. 어플리케이션 구성 요소에 따라 Context의 사용범위가 다르다. 

Application – 실행중인 어플리케이션 프로세스는 Singletone instances 이다. Activity이나 Service에서는 getApplication() 메소드를 통해 getApplicationContext()로 Context를 얻어 올 수 있다. 프로세스내 동일한 instanees를 받게 된다. 

Activity / Service – ContextWrapper를 상속받아 구현한 Context이며 메소드로 Context를 얻어 오면 기본 Context로 구성되어 있다. 프레임워크는 Activity또는 Service가 실행 될때 기본 Context에다 필요한 정보를 warp한다. 실행시 고유한 Context를 가지게 된다. 

BroadcastReceiver – 위의 2가지와 다르게 자기자신이 Context자체는 아니다. 하지만 onRecevie()시 Context를 가져올 수 있는데, 이때의 Context는 ReveiverRestrictedContext이며 두가지 기능 registerReceiver()bindService()를 사용 할 수 없다. 리시버가 브로드캐스트를 처리 할때마다 새로운 Context가 생성 된다. 

ContextProvider – 브로드캐스트와 마찬가지로 자기자신이 Context를 상속 받은것은 아니다. 하지만 액세스후 getContext()를 통해 Context를 가져 올 수 있다. ContextProvider가 동일한 응용프로그램에 대해 호출시, 동일한 응용프로그램의 Singletone instances를 반환하게 된다. 하지만 별도의 프로세스에 있는 경우(서로다른 다른 앱), 프로바이더가 실행되는 응용프로그램의 instances가 반환된다. 

Context 참조

생명주기를 가진 개체또는 클래스에서 Context를 참조 하는 경우 문제가 생길수가 있다. 예를 들어, Acitivy나 Service처럼 생명주기가 있는 클래스에서 Singletone 패턴으로 Context를 참조 하게 되는 경우이다.  

나쁜 예

public class CustomManager {
    private static CustomManager sInstance;
 
    public static CustomManager getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new CustomManager(context);
        }
 
        return sInstance;
    }
 
    private Context mContext;
 
    private CustomManager(Context context) {
        mContext = context;
    }
}

여기에서 라이프사이클이 있는 Activity나 Service에서 Context참조를 유지하는것은 안전하지 않다. sInstance가 정적 참조기이때문이다. 이것은 system에서 개체가 garbage collected가 될수 없음을 의미한다. 즉, 메모리누수가 된다. 

이를 방지 하기위해 ApplicaionConext가 참조되게끔 수정 해야 한다.

좋은 예

public class CustomManager {
    private static CustomManager sInstance;
 
    public static CustomManager getInstance(Context context) {
        if (sInstance == null) {
            //Always pass in the Application Context
            sInstance = new CustomManager(context.getApplicationContext());
        }
 
        return sInstance;
    }
 
    private Context mContext;
 
    private CustomManager(Context context) {
        mContext = context;
    }
}

이처럼 ApplicationContext을 참조로 걸게 되면 Activity또는 Service의 생명주기에 영향을 받지 않고 안전하게 참조할수 있다. ApplicationContext는 singletone자제이기때문에 다른 정적 참조를 하여도 메모리누수가 되지 않는다. 이는 Thread나 Handler등 백그라운드 작업시에서도 ApplicationContext 참조하게 사용하는것 또한 좋은 습관이다. 

Context의 기능

각각의 클래스또는 오브젝트에 따라 사용되는 Context를 정리한것이다.

표를 상황에 따라 최적의 Context를 사용하길 권장한다.

댓글 남기기