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

목차
- Context란 무엇인가?
- Context의 종류
- Context가 제공하는 주요 기능
- Context 사용 시 주의사항
- 다양한 상황에서의 Context 사용 예제
- Context 관련 자주 발생하는 오류와 해결법
- Custom View에서의 Context 활용
- Fragment에서 Context 사용하기
- 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는 다양한 기능을 제공하며, 이러한 기능들은 안드로이드 앱 개발에서 필수적입니다:
- 리소스 접근: 문자열, 이미지, 색상, 레이아웃 등의 앱 리소스에 접근
- 파일 작업: 앱의 내부 저장소나 외부 저장소에 파일을 읽고 쓰기
- 시스템 서비스 접근: 위치 서비스, 알림 관리자, 센서 관리자 등의 시스템 서비스 사용
- Intent 처리: 다른 컴포넌트 시작, 브로드캐스트 전송
- 데이터베이스 접근: SQLite 데이터베이스 생성 및 접근
- SharedPreferences 사용: 간단한 키-값 데이터 저장 및 관리
- 권한 확인: 앱이 특정 권한을 가지고 있는지 확인
#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 관련 문제를 디버깅하는 몇 가지 팁:
- 메모리 누수 감지: LeakCanary 라이브러리를 사용하여 Context 메모리 누수 감지
dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7' }
- Context 유형 확인: 사용 중인 Context가 어떤 유형인지 확인
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"); }
- Context가 유효한지 확인: Activity가 파괴된 후에 Context를 사용하지 않도록 주의
private boolean isContextValid() { if (context instanceof Activity) { Activity activity = (Activity) context; return !activity.isFinishing() && !activity.isDestroyed(); } return context != null; }
#8. Context와 관련된 안드로이드 아키텍처
Context는 안드로이드 아키텍처의 핵심 요소로, 시스템과 앱 간의 상호작용을 가능하게 합니다:
- Context와 ContextWrapper:
- Context는 추상 클래스
- ContextWrapper는 Context를 감싸는 클래스
- Activity, Service, Application은 모두 ContextWrapper를 상속받음
- Context와 앱 컴포넌트:
- 4대 컴포넌트(Activity, Service, ContentProvider, BroadcastReceiver)는 각각 자신의 Context를 가짐
- BroadcastReceiver는 onReceive() 메서드에서 Context를 파라미터로 받음
- ContextThemeWrapper:
- Activity는 ContextThemeWrapper를 상속받아 테마 관련 기능을 추가로 제공
- 따라서 Activity Context는 테마가 적용된 UI 작업에 적합
참고 자료
'■Development■ > 《Android》' 카테고리의 다른 글
[Android] 키보드가 화면 레이아웃에 영향 주지 않게 하는 방법 (완벽 가이드) (0) | 2020.04.08 |
---|---|
[Android] GC_CONCURRENT FREED 라는 에러 메시지 완벽 이해하기 (0) | 2020.04.08 |
[Android] Android 개발의 기본, Activity 완벽 이해하기 (초보자 가이드) 1편 (0) | 2020.04.08 |
[Android ] Intent FLAG 완벽 가이드 : Android 개발자를 위한 필수 지식 (0) | 2020.04.08 |
[Android] ArrayList 객체를 Intent로 전달하는 방법 (3) | 2020.04.08 |