일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- ar vr 차이
- 플러터 설치 2022
- Rxjava Observable
- rxjava hot observable
- 안드로이드 유닛 테스트
- 2022 플러터 설치
- jvm이란
- 큐 자바 코드
- 서비스 쓰레드 차이
- 안드로이드 레트로핏 사용법
- 안드로이드 유닛테스트란
- 2022 플러터 안드로이드 스튜디오
- 자바 다형성
- ANR이란
- 안드로이드 레트로핏 crud
- jvm 작동 원리
- rxjava disposable
- 안드로이드 라이선스 종류
- android ar 개발
- 안드로이드 유닛 테스트 예시
- 안드로이드 os 구조
- 클래스
- android retrofit login
- 안드로이드 라이선스
- 객체
- rxjava cold observable
- 스택 자바 코드
- 서비스 vs 쓰레드
- 스택 큐 차이
- 멤버변수
- Today
- Total
나만을 위한 블로그
[코틀린 코루틴] 7. 코루틴 컨텍스트 본문
launch의 정의를 보면 첫 파라미터가 coroutineContext인 걸 알 수 있다.
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
마지막 인자의 리시버도 CoroutineScope다. 이것은 중요한 개념 같으니 시그니처를 확인한다.
public interface CoroutineScope {
public val coroutineContext: CoroutineContext
}
@SinceKotlin("1.3")
public interface Continuation<in T> {
public val context: CoroutineContext
public fun resumeWith(result: Result<T>)
}
Continuation도 CoroutineContext를 갖고 있다. 코루틴에서 가장 중요한 요소들이 모두 갖고 있는데 이건 무엇인가?
CoroutineContext 인터페이스
CoroutineContext는 원소나 원소의 집합을 나타내는 인터페이스다. CoroutineName, CoroutineDispatcher 등 Element 객체들이 인덱싱된 집합이란 점에서 Map, Set 같은 컬렉션과 비슷하다. 특이한 건 각 Element도 CoroutineContext라는 것이다.
컨텍스트에서 모든 원소는 식별 가능한 유일한 key를 갖고 있으며 각 key는 주소로 비교된다. CoroutineName이나 Job은 CoroutineContext 인터페이스를 구현한 CoroutineContext.Element를 구현한다
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Job
import kotlin.coroutines.CoroutineContext
fun main() {
val name: CoroutineName = CoroutineName("A name")
val element: CoroutineContext.Element = name
val context: CoroutineContext = element
val job: Job = Job()
val jobElement: CoroutineContext.Element = job
val jobContext: CoroutineContext = jobElement
}
CoroutineContext에서 원소 찾기
CoroutineContext는 컬렉션과 비슷하게 get으로 유일한 키를 가진 원소를 찾을 수 있다. 대괄호를 쓰는 것도 가능하다.
원소가 존재하지 않으면 null을 리턴한다.
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Job
import kotlin.coroutines.CoroutineContext
fun main() {
val ctx: CoroutineContext = CoroutineName("A name")
val coroutineName: CoroutineName? = ctx[CoroutineName] // ctx.get(CoroutineName)
println(coroutineName?.name)
val job: Job? = ctx[Job]
println(job)
}
// A name
// null
컨텍스트 더하기
CoroutineContext의 유용한 기능은 2개의 CoroutineContext를 합쳐서 하나의 CoroutineContext로 만드는 것이다.
다른 키를 가진 두 원소를 더해서 만들어진 컨텍스트는 키 2개를 모두 갖고 있다.
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Job
import kotlin.coroutines.CoroutineContext
fun main() {
val ctx1: CoroutineContext = CoroutineName("Name1")
println(ctx1[CoroutineName]?.name) // Name1
println(ctx1[Job]?.isActive) // null
val ctx2: CoroutineContext = Job()
println(ctx2[CoroutineName]?.name) // null
println(ctx2[Job]?.isActive) // Active 상태라서 true
// 빌더를 통해 생성되는 Job의 기본 상태가 Active가 되어 true가 된다
val ctx3 = ctx1 + ctx2
println(ctx3[CoroutineName]?.name) // Name1
println(ctx3[Job]?.isActive) // true
}
비어 있는 코루틴 컨텍스트
앞서 말했듯 CoroutineContext는 컬렉션과 비슷해서 빈 컨텍스트도 만들 수 있다. 빈 컨텍스트는 원소가 없기 때문에 다른 컨텍스트에 더해도 변화가 없다.
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Job
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
fun main() {
val empty: CoroutineContext = EmptyCoroutineContext
println(empty[CoroutineName]) // null
println(empty[Job]) // null
val ctxName = empty + CoroutineName("Name1") + empty
println(ctxName[CoroutineName]) // CoroutineName(Name1)
}
원소 제거
minusKey()에 키를 넣어서 특정 원소를 컨텍스트에서 제거할 수 있다.
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Job
fun main() {
val ctx = CoroutineName("Name1") + Job()
println(ctx[CoroutineName]?.name) // Name1
println(ctx[Job]?.isActive) // true
val ctx2 = ctx.minusKey(CoroutineName)
println(ctx2[CoroutineName]?.name) // null
println(ctx2[Job]?.isActive) // true
val ctx3 = (ctx + CoroutineName("Name2"))
.minusKey(CoroutineName)
println(ctx3[CoroutineName]?.name) // null
println(ctx3[Job]?.isActive) // true
}
컨텍스트 폴딩
fold는 컬렉션의 fold와 비슷하게 아래를 요구한다.
- 누산기의 1번째 값
- 누산기의 현재 상태와 현재 실행되고 있는 원소로 누산기의 다음 상태를 계산할 연산
코루틴 컨텍스트, 빌더
CoroutineContext는 코루틴의 데이터를 저장, 전달하는 방법이다. 부모-자식 관계의 영향으로 인해 부모는 컨텍스트를 자식에게 전달하고, 자식은 부모로부터 컨텍스트를 상속받는다.
모든 자식은 코루틴 빌더의 인자에 정의된 컨텍스트를 가질 수 있다. 이 때 인자로 전달된 컨텍스트는 부모로부터 상속받은 컨텍스트를 대체한다.
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
fun main() = runBlocking(CoroutineName("main")) {
log("Started")
val v1 = async(CoroutineName("c1")) {
delay(500)
log("Running async")
42
}
launch(CoroutineName("c2")) {
delay(1000)
log("Running launch")
}
log("answer : ${v1.await()}")
}
fun CoroutineScope.log(msg: String) {
val name = coroutineContext[CoroutineName]?.name
println("[$name] $msg")
}
// [main] Started
// [c1] Running async
// [main] answer : 42
// [c2] Running launch
새 원소가 같은 키를 가진 이전 원소를 대체하기 때문에, 자식의 컨텍스트는 부모한테서 상속받은 컨텍스트 중 같은 키를 가진 원소를 대체한다.
중단 함수에서 컨텍스트에 접근하기
CoroutineScope는 컨텍스트 접근 시 사용하는 coroutineContext 프로퍼티를 갖고 있다.
일반적인 중단 함수에서 컨텍스트에 접근하는 방법은 중단 함수 사이에 전달되는 Continuation 객체를 사용해 중단 함수에서 부모의 컨텍스트에 접근하는 것이다.
coroutineContext 프로퍼티는 모든 중단 스코프에서 쓸 수 있고, 이를 통해 컨텍스트에 접근할 수 있다.
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.coroutines.coroutineContext
suspend fun main() = withContext(CoroutineName("Outer")) {
printName()
launch(CoroutineName("Inner")) {
printName()
}
delay(10)
printName()
}
suspend fun printName() = println(coroutineContext[CoroutineName]?.name)
// Outer
// Inner
// Outer
'책 > 코틀린 코루틴' 카테고리의 다른 글
[코틀린 코루틴] 9. 취소 (0) | 2024.02.24 |
---|---|
[코틀린 코루틴] 8. Job, 자식 코루틴 기다리기 (0) | 2024.02.17 |
[코틀린 코루틴] 6. 코루틴 빌더 (0) | 2024.02.16 |
[코틀린 코루틴] 3. 중단은 어떻게 작동하는가 (0) | 2024.02.14 |
[코틀린 코루틴] 2. 시퀀스 빌더 (0) | 2024.02.12 |