본문 바로가기
■Development■/《Android》

[Android] 안드로이드 개발의 핵심, Context 완벽 이해하기(초보자 가이드)

by 은스타 2020. 4. 8.
반응형

안드로이드 개발의 핵심, Context 완벽 이해하기 (초보자 가이드)

안녕하세요.
안드로이드 개발을 시작하면 가장 자주 마주치게 되는 개념 중 하나가 바로 'Context'입니다. 거의 모든 안드로이드 API 호출에서 등장하는 이 Context는 무엇이고, 왜 이렇게 중요할까요? 이 글에서는 초보 개발자도 쉽게 이해할 수 있도록 Context의 개념부터 올바른 사용법까지 상세히 알아보겠습니다.


목차

 

#1. Context란 무엇인가?

Context는 직역하면 '맥락', '문맥'이라는 의미를 가지고 있습니다. 안드로이드에서 Context는 현재 앱의 환경에 대한 정보와 시스템 리소스 및 서비스에 접근할 수 있는 인터페이스를 제공합니다. 쉽게 말해, Context는 앱이 실행되는 환경에 대한 모든 정보를 담고 있는 객체라고 볼 수 있습니다.

안드로이드 공식 문서에서는 Context를 다음과 같이 정의합니다:

"인터페이스를 구현하고 앱의 특정 환경에 대한 정보를 제공하는 추상 클래스."

 

여기서 중요한 점은 Context가 추상 클래스라는 것입니다. 즉, Context 자체는 직접 사용할 수 없고, 이를 구현한 구체적인 클래스를 통해 사용합니다. 대표적으로 Activity, Service, Application 등이 Context를 상속받아 구현하고 있습니다.

 

#2. Context의 종류

Context는 크게 두 가지 유형으로 나눌 수 있습니다:

Application Context

Application Context는 애플리케이션의 전체 생명주기와 연결된 Context입니다. 앱이 실행되는 동안 항상 존재하며, 애플리케이션 전체에 관련된 작업을 수행할 때 사용됩니다.

주요 특징:

  • 앱이 실행되는 동안 항상 유지됨
  • Activity나 Service의 생명주기와 무관하게 사용 가능
  • getApplicationContext()를 통해 어디서든 접근 가능

사용 예시:

// Java
Context appContext = getApplicationContext();
// Kotlin
val appContext = applicationContext

Activity Context

Activity Context는 특정 Activity의 생명주기와 연결된 Context입니다. UI 작업이나 현재 Activity와 관련된 작업을 수행할 때 사용됩니다.

주요 특징:

  • 해당 Activity의 생명주기와 동일하게 유지됨
  • Activity 내에서는 this로 접근 가능
  • UI 관련 작업에 적합

사용 예시:

// Java - Activity 내부에서
Context activityContext = this;
// 또는
Context activityContext = MainActivity.this;
// Kotlin - Activity 내부에서
val activityContext = this
// 또는
val activityContext = this@MainActivity

 

#3. Context가 제공하는 주요 기능

Context는 다양한 기능을 제공하며, 이러한 기능들은 안드로이드 앱 개발에서 필수적입니다:

  1. 리소스 접근: 문자열, 이미지, 색상, 레이아웃 등의 앱 리소스에 접근
  2. 파일 작업: 앱의 내부 저장소나 외부 저장소에 파일을 읽고 쓰기
  3. 시스템 서비스 접근: 위치 서비스, 알림 관리자, 센서 관리자 등의 시스템 서비스 사용
  4. Intent 처리: 다른 컴포넌트 시작, 브로드캐스트 전송
  5. 데이터베이스 접근: SQLite 데이터베이스 생성 및 접근
  6. SharedPreferences 사용: 간단한 키-값 데이터 저장 및 관리
  7. 권한 확인: 앱이 특정 권한을 가지고 있는지 확인

 

#4. Context 사용 시 주의사항

메모리 누수(Memory Leak) 방지하기

Context를 잘못 사용하면 메모리 누수가 발생할 수 있습니다. 특히 Activity Context를 정적(static) 변수에 저장하거나, 오래 살아있는 객체에 저장할 경우 문제가 될 수 있습니다.

잘못된 예시:

// 메모리 누수 위험이 있는 코드
public class MyManager {
    private static Context context;

    public static void init(Context context) {
        MyManager.context = context; // Activity Context를 정적 변수에 저장
    }
}

개선된 예시:

// 메모리 누수를 방지하는 코드
public class MyManager {
    private static Context context;

    public static void init(Context context) {
        // Activity가 아닌 Application Context 사용
        MyManager.context = context.getApplicationContext();
    }
}

올바른 Context 선택하기

작업의 성격에 따라 적절한 Context를 선택하는 것이 중요합니다:

작업 권장 Context 설명
Toast 표시 Application 어디서든 사용 가능
새 Activity 시작 Activity UI 흐름 유지를 위해
레이아웃 인플레이션 Activity 테마와 스타일 적용을 위해
시스템 서비스 사용 Application 생명주기가 긴 작업에
SharedPreferences Application 앱 전체에서 공유되는 데이터에
데이터베이스 접근 Application 백그라운드 작업에

다음은 상황별 올바른 Context 선택에 대한 참고 표입니다:

작업 유형 Applcation Context Activity Context
다이얼로그 표시
Activity 시작 ✅ (단, FLAG_NEW_TASK 필요)
레이아웃 인플레이션 ❌ (테마 적용 X)
토스트 표시
BroadcastReceiver
등록
리소스 접근
파일 작업
SharedPreferences
ContentResolver

 

#5. 다양한 상황에서의 Context 사용 예제

리소스 접근하기

문자열, 이미지, 색상 등의 리소스에 접근할 때 Context를 사용합니다:

// 문자열 리소스 접근
String appName = context.getString(R.string.app_name);

// 색상 리소스 접근
int color = context.getColor(R.color.colorPrimary);

// 드로어블 리소스 접근
Drawable icon = context.getDrawable(R.drawable.ic_launcher);

// 레이아웃 인플레이션
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.item_layout, parent, false);

시스템 서비스 사용하기

다양한 시스템 서비스는 Context를 통해 접근합니다:

// 위치 서비스 접근
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

// 알림 서비스 접근
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

// 알람 서비스 접근
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

// 클립보드 서비스 접근
ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);

새 Activity 시작하기

Intent를 사용하여 새 Activity를 시작할 때 Context가 필요합니다:

// 새 Activity 시작하기
Intent intent = new Intent(context, SecondActivity.class);
intent.putExtra("key", "value");
context.startActivity(intent);

// 결과를 기대하는 Activity 시작 (Activity Context 필요)
// Activity에서만 사용 가능
Intent pickImageIntent = new Intent(Intent.ACTION_PICK);
pickImageIntent.setType("image/*");
startActivityForResult(pickImageIntent, REQUEST_IMAGE_PICK);

Toast 메시지 표시하기

Toast 메시지를 표시할 때도 Context가 필요합니다:

// 간단한 토스트 메시지
Toast.makeText(context, "Hello, World!", Toast.LENGTH_SHORT).show();

// 커스텀 토스트 메시지
Toast toast = new Toast(context);
View view = LayoutInflater.from(context).inflate(R.layout.custom_toast, null);
toast.setView(view);
toast.setDuration(Toast.LENGTH_LONG);
toast.show();

SharedPreferences 사용하기

간단한 데이터를 저장하고 불러올 때 SharedPreferences를 사용합니다:

// SharedPreferences 데이터 저장
SharedPreferences preferences = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("username", "user123");
editor.putBoolean("isLoggedIn", true);
editor.apply();

// SharedPreferences 데이터 읽기
SharedPreferences preferences = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
String username = preferences.getString("username", "");
boolean isLoggedIn = preferences.getBoolean("isLoggedIn", false);

데이터베이스 접근하기

SQLite 데이터베이스를 사용할 때도 Context가 필요합니다:

// SQLiteOpenHelper 사용
public class MyDatabaseHelper extends SQLiteOpenHelper {
    public MyDatabaseHelper(Context context) {
        super(context, "my_database.db", null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // 테이블 생성
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 업그레이드 로직
    }
}

// 데이터베이스 사용
MyDatabaseHelper dbHelper = new MyDatabaseHelper(context);
SQLiteDatabase db = dbHelper.getWritableDatabase();

 

#6. Context 관련 자주 발생하는 오류와 해결법

1. IllegalStateException: Not allowed to start activity from outside

문제: Application Context로 새 Activity를 시작하려고 할 때 발생

해결법:

// Application Context로 Activity 시작 시
Intent intent = new Intent(context, SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);

2. BadTokenException: Unable to add window

문제: Application Context로 Dialog를 표시하려고 할 때 발생

해결법:

// 항상 Activity Context 사용하기
AlertDialog.Builder builder = new AlertDialog.Builder(activityContext);
builder.setTitle("제목")
       .setMessage("내용")
       .setPositiveButton("확인", null)
       .show();

3. 메모리 누수(Memory Leak)

문제: Activity Context를 정적 변수나 싱글톤에 저장할 때 발생

해결법:

// WeakReference 사용하기
public class MyManager {
    private WeakReference<Context> contextRef;

    public MyManager(Context context) {
        this.contextRef = new WeakReference<>(context);
    }

    public void doSomething() {
        Context context = contextRef.get();
        if (context != null) {
            // context 사용
        }
    }
}

Custom View에서의 Context 활용

커스텀 뷰를 만들 때 Context는 필수적으로 사용됩니다:

public class MyCustomView extends View {
    private Paint paint;

    public MyCustomView(Context context) {
        super(context);
        init(context);
    }

    public MyCustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        paint = new Paint();
        paint.setColor(context.getColor(R.color.colorAccent));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 그리기 로직
    }
}

Fragment에서 Context 사용하기

Fragment는 Context를 직접 상속받지 않지만, 연결된 Activity의 Context에 접근할 수 있습니다:

public class MyFragment extends Fragment {
    private Context context;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        this.context = context;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_my, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        // Context 사용 예시
        Toast.makeText(context, "Fragment Created", Toast.LENGTH_SHORT).show();

        // 또는 getContext() 메서드 사용
        if (getContext() != null) {
            Toast.makeText(getContext(), "Fragment Created", Toast.LENGTH_SHORT).show();
        }
    }
}

 

#7. Context 디버깅 팁

Context 관련 문제를 디버깅하는 몇 가지 팁:

  1. 메모리 누수 감지: LeakCanary 라이브러리를 사용하여 Context 메모리 누수 감지
  2. dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7' }
  3. Context 유형 확인: 사용 중인 Context가 어떤 유형인지 확인
  4. if (context instanceof Activity) { Log.d("ContextCheck", "Activity Context"); } else if (context instanceof Application) { Log.d("ContextCheck", "Application Context"); } else { Log.d("ContextCheck", "Other Context"); }
  5. Context가 유효한지 확인: Activity가 파괴된 후에 Context를 사용하지 않도록 주의
  6. private boolean isContextValid() { if (context instanceof Activity) { Activity activity = (Activity) context; return !activity.isFinishing() && !activity.isDestroyed(); } return context != null; }

 

#8. Context와 관련된 안드로이드 아키텍처

Context는 안드로이드 아키텍처의 핵심 요소로, 시스템과 앱 간의 상호작용을 가능하게 합니다:

  1. Context와 ContextWrapper:
  • Context는 추상 클래스
  • ContextWrapper는 Context를 감싸는 클래스
  • Activity, Service, Application은 모두 ContextWrapper를 상속받음
  1. Context와 앱 컴포넌트:
  • 4대 컴포넌트(Activity, Service, ContentProvider, BroadcastReceiver)는 각각 자신의 Context를 가짐
  • BroadcastReceiver는 onReceive() 메서드에서 Context를 파라미터로 받음
  1. ContextThemeWrapper:
  • Activity는 ContextThemeWrapper를 상속받아 테마 관련 기능을 추가로 제공
  • 따라서 Activity Context는 테마가 적용된 UI 작업에 적합

 

참고 자료

  1. 안드로이드 공식 문서 - Context
  2. 안드로이드 공식 문서 - Application
  3. 안드로이드 디자인 패턴 - Context 활용
  4. Stack Overflow - Context란 무엇인가?
  5. 메모리 누수 방지 가이드
  6. Context 유형별 적절한 사용법
  7. Google I/O - Memory Management for Android Apps
  8. LeakCanary - 메모리 누수 감지 라이브러리
반응형