[Android] 컨텍스트(Context) 정리
*context : 맥락, 전후 사정
컨텍스트(Context)
- public abstract class Context { }
- 애플리케이션의 현재 상태의 맥락.
- 현재 애플리케이션 자체라고 생각하기.
- 애플리케이션 환경에 대한 전역 정보를 얻기 위한 방법을 제공한다.
- 애플리케이션별 리소스, 클래스 등에 대한 접근을 허용, 액티비티 실행, 브로드캐스트, 인텐트 수신 등과 같은 애플리케이션 수준 작업에 사용된다.
- 액티비티와 애플리케이션에 대한 정보를 얻기 위해 사용할 수 있다.
컨텍스트(Context)로 할 수 있는 것
- 리소스 로딩(색상, 이미지, 문자열, 사운드 등)
- 새 액티비티 실행을 위한 인텐트 생성
- 뷰 생성
- 시스템 서비스 요청 등
컨텍스트(Context) 코드 예
Intent 생성자 파라미터
> public Intent(Context packageContext, Class<?> cls)
val intent: Intent = Intent(this, DetailActivity::class.java)
startActivity(intent)
Toast 메시지 생성 파라미터
> open static fun makeText(context: Context!, text: CharSequence!, duration: Int): Toast!
Toast.makeText(this, "종료하려면 한 번 더 누르세요.", Toast.LENGTH_SHORT)
애플리케이션 컨텍스트(Application Context)
- 액티비티에서 getApplicationContext()로 접근. (=applicationContext 프로퍼티)
- 애플리케이션의 라이프 사이클과 연결되어있음.
- 액티비티의 범위를 넘어서 컨텍스트를 전달하거나, 현재의 컨텐스트와 분리된 라이프사이클을 가진 컨텍스트가 필요할 때 사용
- 싱글턴의 경우 애플리케이션 컨텍스트를 전달,
액티비티나 서비스 이외의 객체에서 컨텍스트를 참조해야 할 때 애플리케이션 컨텍스트를 사용. - 가비지 컬렉션 X, 메모리 누수 주의, 프로세스가 살아있는 동안 남아있음.
액티비티 컨텍스트(Activity Context)
- 액티비티 내에서 유요한 컨텍스트
- 액티비티의 라이프사이클과 연결되어 있음.
- 액티비티 범위 내에서 컨텍스트를 전달하거나, 현재의 컨텍스트에 종속된 라이프사이클을 가진 컨텍스트가 필요할 때 사용.
- Toast, Dialog 등 UI 작업에는 액티비티 컨텍스트 전달.
- 가비지 컬렉션 O, 액티비티가 소멸하면 함께 소멸된다.
- 일반적으로 위치에서 가장 가까운, 스코프에 해당하는 컨텍스트를 사용. (액티비티에서는 액티비티 컨텍스트를, 애플리케이션에서는 애플리케이션 컨텍스트를 사용)
Context 클래스 상수 (일부)
Context 클래스에 정의되어 있는 상수들은 일반적으로 getSystemService(String name) 함수의 매개변수로 사용되어, 시스템 장치나 안드로이드 프레임워크 내 기능과 상화작용하기 위한 '~Manager' 객체를 얻는데 쓰인다.
getSystemService(String name) : 매개변수에 대응되는 안드로이드가 제공하는 시스템 레벨 서비스를 요청한다.
타입 | 상수 | 설명 / 반환 객체 |
String | ACTIVITY_SERVICE | 전역 시스템 상태와 상호작용하기 위한 ActivityManager |
String | ALARM_SERVICE | 선택한 시간에 인텐트를 발생하는 AlarmManager |
String | AUDIO_SERVICE | 볼륨, 알림 모드, 오디오 라우팅을 위한 AudioManager |
String | BATTERY_SERVICE | 배터리 상태를 관리하는 BatteryManager |
String | BLUETOOTH_SERVICE | 블루투스 사용을 위한 BluetoothManager |
String | CAMERA_SERVICE | 카메라 장치와 상호작용하기 위한 CameraManager |
String | CLIPBOARD_SERVICE | 전역 클립보드 내용에 접근하고 수정하기 위한 ClipboardManager |
String | CONNECTIVITY_SERVICE | 네트워크 연결을 관리하기 위한 ConnectivityManager |
String | DISPLAY_SERVICE | 디스플레이 장치와 상호작용하기 위한 DisplayManager |
String | DOWNLOAD_SERVICE | HTTP 다운로드를 요청하기 위한 DownladManager |
String | INPUT_SERVICE | 입력 장치와 상호작용하기 위한 InputManager |
String | LAYOUT_INFLATER_SERVICE | 현재 컨텍스트의 레이아웃 리소스를 인플레이트하는 LayoutInflater |
String | LOCATION_SERVICE | 위치 정보를 관리하는 LocationManager |
String | NOTIFICATION_SERVICE | 백그라운드 이벤트를 사용자에게 알리기 위한 NotificationManger |
String | SENSOR_SERVICE | 센서에 접근하기 위한 SensorManager |
String | WIFI_SERVICE | WiFi 접근을 관리하는 WifiManager |
String | WINDOW_SERVICE | 시스템 윈도우 관리자에 접근하기 위한 WindowManager |
String | BUGREPORT_SERVICE | 버그리포트를 포착하는 서비스 |
int | BIND_AUTO_CREATE | 서비스 컴포넌트 실행에 사용되는 bindService(Intent, ServiceConnection, int) 함수를 위한 플래그. 바인딩이 존재하는 한 서비스를 자동 생성하도록 설정 |
ex) NOTIFICATION_SERVICE 요청해서 NotificationManager 객체 얻기
상태바(Status Bar)는 시스템 영역이기 때문에 외부 앱에서 푸시알림을 띄우려면 시스템에서 알림 서비스를 요청해야 한다.
// 버튼 클릭 시 알림 띄우기
binding.notificationButton.setOnClickListener {
// 시스템 알림을 관리하는 NotificationManager 얻기
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val builder: NotificationCompat.Builder
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = "one-channel"
val channelName = "My Channel One"
val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT).apply {
description = "My Channel One Description"
setShowBadge(true)
val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val audioAttributes = AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ALARM)
.build()
setSound(uri, audioAttributes)
enableLights(true)
}
// NotificationManager에 채널 등록
manager.createNotificationChannel(channel)
builder = NotificationCompat.Builder(this, channelId)
} else {
builder = NotificationCompat.Builder(this)
}
builder.run {
setSmallIcon(R.drawable.small)
setWhen(System.currentTimeMillis())
setContentTitle("홍길동")
setContentText("안녕하세요.")
setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.big))
}
// NotificationManger로 알림 출력
manager.notify(11, builder.build())
Context 클래스 메서드 (일부)
Activity, BroadcastReceiver, Service 컴포넌트를 실행하고 중단하는 함수, Application에 대한 함수.
(코틀린에서는 getApplicationContext는 applicationContext로, getResources()는 resources와 같이 메서드가 아니라 프로퍼티로 접근한다. 메서드를 쓰든 프로퍼티를 쓰든 반환되는 값은 같음.)
반환 타입 | 메서드 | 설명 |
boolean | bindService(Intent service, ServiceConnextion conn, int flags) | 애플리케이션 서비스에 연결하며 필요한 경우 생성 |
Context | getApplicationContext() | 프로세스의 싱글 전역 Context 반환 |
ApplicationInfo | getApplicationInfo() | 컨텍스트 패키지에 대한 전체 애플리케이션 정보 반환 |
AssetManager | getAssets() | 애플리케이션 패키지에 대한 AssetManager 객체 반환 |
AttributionSource | getAttributionSource() | AttributionSource 반환 |
int | getColor(int id) | 특정 리소스 id에 연결된 컬러를 반환하고 현재 테마에 맞게 스타일화 |
Display | getDisplay() | 컨텍스트에 연결된 디스플레이 반환 |
Drawable | getDrawable(int id) | 특정 리소스 id에 연결된 Drawable 객체를 반환하고 현재 테마에 맞게 스타일화 |
String | getPackageName() | 애플리케이션 패키지명 반환 |
Resource | getResources() | 애플리케이션 패키지에 대한 리소스 인스턴스를 반환 |
String | getString(int resId) | 애플리케이션 패키지의 기본 문자열 테이블로 부터 지역화된 문자열을 반환 |
Object | getSystemService(String name) | 시스템 레벨 서비스의 핸들을 반환 |
Intent | registerReceiver(BroadcastReceiver receiver, IntentFilter filter) | 메인 액티비티 스레드에서 실행할 브로드캐스트 리시버를 등록 |
void | sendBroadcast(Intent intent) | 브로드캐스트를 실행하는 인텐트를 시스템에 전달 |
void | startActivity(Intent intent) | 새 액티비티 실행 |
ComponentName | startService(Intent service) | 주어진 서비스를 중단하도록 요청 |
boolean | stopService(Intent service) | 주어진 서비스를 시작하도록 요청 |
void | unbindService(ServiceConnection conn) | 서비스 연결 끊기 |
Q. Context 클래스에 선언되어 있는 상수 필드, 함수들을 클래스명.필드명, 클래스명.함수명() 과 같이 호출하지 않고, Activity 코드 내에서 바로 필드명, 함수명()으로 바로 쓸 수 있는 이유는?
A. Activity 클래스가 Context 클래스를 상속받기 때문이다.
메인 액티비티 코드는 Activity클래스를 상속받아 액티비티 컴포넌트를 구현하는 과정인데, 이미 Activity클래스가 Context클래스를 상속 받아서 만들어진 클래스이기 때문에 MainActivity 까지 Context가 가진 필드, 함수들이 그대로 상속되기 때문에 Context.getApplicationContext()와 같이 쓰지 않고 getApplicationContext()로 바로 함수를 호출할 수 있다. (=부모 클래스(Context)를 상속 받았다는 것은 자식 클래스(MainActivity)가 부모 클래스가 가진 모든 멤버를 물려받아 가지고 있다는 뜻이기 때문에)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// Context 클래스의 getApplicationContext() 함수
var appcontext = applicationContext
binding.notificationButton.setOnClickListener {
// Context 클래스의 getSystemService() 함수
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val builder: NotificationCompat.Builder
/** 생략 **/
}
}
}
Context 객체 얻기
- getContext() ← ContentProvider 클래스 상속
- getBaseContext() ← ContextWrapper 클래스 상속
- getApplicationContext() ← Context 클래스 상속
- this ← 현재 클래스 객체를 가리키는 키워드
*MainActivity ← Activity ← Context 관계로 상속받기 때문에 MainActivity 클래스 본인도 Context라고 볼 수 있음.
참고자료
- Android Developer - Context, https://developer.android.com/reference/android/content/Context
- Youtube - What does context mean in Android, https://youtu.be/JzewiQixgRI ★