개요
오늘은 CoroutineContext에 대한 내용을 정리해보겠다.
1. CoroutineContext란?
코루틴의 실행 환경을 설정하고 관리하는 인터페이스로 여러 개의 요소를 포함하는 키-값(key-value) 쌍의 집합이다.
CoroutineContext를 통해 실행 방식, 취소 여부, 예외 처리 등을 조정할 수 있다.
1-1. CoroutineContext의 구성 요소
주요한 구성 요소는 네 가지가 있다.
1) CoroutineName
-> 코루틴의 이름을 설정한다.
2) CoroutineDispatcher
-> 코루틴을 스레드에 할당햏 실행한다.
3) Job
-> 코루틴의 추상체로 코루틴을 조작하는 데 사용된다.
4) CoroutineExceptionHandler
-> 코루틴에서 발생한 예외를 처리한다.
2. CoroutineContext 구성
CoroutineContext 객체는 위에서 언급한 것처럼 키-값 쌍으로 각 구성 요소를 관리한다.
키 | 값 |
CoroutineName 키 | CoroutineName 객체 |
CoroutineDispatcher 키 | CoroutineDispatcher 객체 |
Job 키 | Job 객체 |
CoroutineExceptionHandler 키 | CoroutineExceptionHandler 객체 |
키에 대한 중복 값은 허용되지 않기 떄문에 각 구성 요소의 객체를 한 개씩만 가질 수 있다.
2-1. CoroutineContext 구성 요소 추가
(+) 연산자 사용
CoroutineContext 객체 간에 더하기(+) 연산자를 사용해 CoroutineContext 객체를 구성한다.
val coroutineContext : CoroutineContext = newSingleThreadContext("MyThread") +
CoroutineName("MyCoroutine")
-> CoroutineName이 CoroutineName("MyCoroutine")으로 설정되고,
-> CoroutineDispatcher 값이 newSingleThreadContext("MyThread")로 설정된
-> CoroutineContext가 구성된다.
이 만들어진 CoroutineContext를 launch 빌더 함수의 context 인자로 넘겨주면 된다.
fun main() = runBlocking<Unit> {
val coroutineContext : CoroutineContext = newSingleThreadContext("MyThread") +
CoroutineName("MyCoroutine")
launch(context = coroutineContext) {
println("[${Thread.currentThread().name} ${this.coroutineContext[CoroutineName]}] 실행")
}
}
2-2. CoroutineContext 덮어씌우기
CoroutineContext 객체에 같은 구성 요소가 둘 이상 더해지게 된다면 나중에 추가된 CoroutineContext 구성 요소가 이전의 값을 덮어씌운다.
fun main() = runBlocking<Unit> {
val coroutineContext : CoroutineContext = newSingleThreadContext("MyThread") +
CoroutineName("MyCoroutine")
val newCoroutineContext : CoroutineContext = coroutineContext + CoroutineName("NewCoroutine")
launch(context = newCoroutineContext) {
println("[${Thread.currentThread().name} ${this.coroutineContext[CoroutineName]}] 실행")
}
}
기존 coroutineContext에 CoroutineName을 추가했더니, 새로운 CoroutineName으로 덮어씌워졌다.
3. CoroutineContext 구성 요소 접근
CoroutineContext 구성 요소는 자신의 내부에 키를 싱글톤 객체로 구현한다.
public data class CoroutineName(
val name: String
) : AbstractCoroutineContextElement(CoroutineName) {
public companion object Key : CoroutineContext.Key<CoroutineName>
override fun toString(): String = "CoroutineName($name)"
}
CoroutineName 클래스를 살펴보자면 CoroutineContext.key<CoroutineName>을 구현하는 동반 객체 Key가 있다.
이 Key를 사용한다면 CoroutineContext에서 CoroutineName에 접근할 수 있다.
다른 구성 요소도 이와 마찬가지이다.
@OptIn(ExperimentalStdlibApi::class)
fun main() = runBlocking<Unit> {
val coroutineContext = CoroutineName("MyCoroutine") + Dispatchers.IO
val nameFromContext = coroutineContext[CoroutineName.Key]
val dispatcherFromContext = coroutineContext[CoroutineDispatcher]
println(nameFromContext)
println(dispatcherFromContext)
}
-> 대괄호 안에 CoroutineName.Key를 통해서도 접근이 가능하지만
-> Key를 빼도 동일하게 작동한다.
4. CoroutineContext 구성 요소 제거
구성 요소를 제거하기 위해 minusKey 함수를 제공한다.
fun main() = runBlocking<Unit> {
val coroutineName = CoroutineName("MyCoroutine")
val dispatcher = Dispatchers.IO
val myJob = Job()
val coroutineContext : CoroutineContext = coroutineName + dispatcher + myJob
val deletedCoroutineContext = coroutineContext.minusKey(CoroutineName)
println(deletedCoroutineContext[CoroutineName])
}
coroutineContext에서 minusKey를 활용해 CoroutineName을 삭제했더니, null로 뜨는 것을 알 수 있다.
단, minusKey를 호출한 CoroutineContext 객체( = coroutineContext)는 그대로 유지되고, 제거된 새로운 CoroutineContext를 deletedCoroutineContext에 반환한다는 것이다!
'Android > Coroutine' 카테고리의 다른 글
[Coroutine] async와 Deferred (0) | 2025.03.18 |
---|---|
[Coroutine] 코루틴 취소 및 Job 상태 (0) | 2025.03.13 |
[Coroutine] 코루틴 빌더와 Job (0) | 2025.03.12 |
[Coroutine] Coroutine Dispatcher (0) | 2025.03.04 |
[Coroutine] 코루틴이란? (0) | 2025.03.04 |