Coroutine Cancel
val job = launch {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancel() // cancels the job
job.join() // waits for job's completion
println("main: Now I can quit.")
// reulst
job: I'm sleeping 0 ...
job: I'm sleeping 1 ...
job: I'm sleeping 2 ...
main: I'm tired of waiting!
main: Now I can quit.
보통 suspend 함수 내부적으로 isActive를 체크하여 false일때 Exception을 던진다.
if (resumeMode == MODE_CANCELLABLE) {
val job = context[Job]
if (job != null && !job.isActive) {
val cause = job.getCancellationException()
cancelResult(state, cause)
throw recoverStackTrace(cause, this)
}
}
cancle()의 타겟인 코루틴 내부에 suspend 함수가 있다면, 해당 suspend함수가 CancellationExceoption을 thorw한다. 만약 suspend함수가 없다면 isActive를 체크하여 명시적으로 해당 익셉션을 던지도록 한다.
Coroutine TimeOut
코루틴을 일일히 cancel하지 않고, 특정 시간이후에 취소하도록 할때 withTimeout을 사용하여 취소하도록 할 수 있다.
try {
withTimeout(3000) {
playerRepository.productClick(
memberId,
partnerId,
convertBroadCastId(),
productId,
playerEnterTime,
isExhibitPartner,
isExhibitSauceFlex,
"Android",
makeElkData(
KinesisAPIDataModel(
roomId,
currentTime,
playingState,
sessionId,
userAgent
).convertJsonToString()
)
)
.flowOn(Dispatchers.IO)
.onCompletion {
_isLoading.value = false
isProductEnable = true
}
.catch {}
.collect {
(context as PlayerActivity).showMinimizeActionView(
context,
it.response.redirectUrl
)
}
}
} catch (e: TimeoutCancellationException) {
_isLoading.value = false
isProductEnable = true
}
withTimout은 TimeoutCancellationException 익셉션을 발생시키면서 종료되기에 try-catch문으로 묶어서 사용해야 앱이 중지 되는걸 방지할 수 있다.
Coroutine Suspend Function
CoroutineContext
public operator fun <E : Element> get(key: Key<E>): E?
- get() - 연산자(operator) 함수의 의미로, 매개변수로 주어진 key에 해당하는
Context 요소를 반환하는 함수
public fun <R> fold(initial: R, operation: (R, Element) -> R): R
- fold() - 초기값(initalVlaue)을 시작으로 제공된 병합 함수(operation)를 이용해서
대상 컨텍스트 요소들을 병합한 후 결과를 반환하는 함수
public operator fun plus(context: CoroutineContext): CoroutineContext = ...impl...
- plus() - 현재(기존) Context와 파라미터로 주어진 context가 갖는 요소(Element)들을
모두 포함하는 새로운 Context를 반환
public fun minusKey(key: Key<*>): CoroutineContext
- minusKey() - 현재(기존) Context에서 주어진 key를 갖는 요소(Element)를 제외한
새로운 Context를 반환
Key / Element?
코루틴컨텍스트 구현체
코루틴 컨텍스트는 인터페이스로 이를 구현한 구현체는 기본적으로 3가지의 종류가 존재
1) EmptyCoroutineContext - Singleton(싱글톤), Default Context
Context를 명시하지 않을 경우 기본적으로 적용되는 GlobalScope에 사용된 Context로, GlobalScope == launc{ }는 같은 싱글톤 EmptyContext를 사용하는 의미이다
2) CombinedContext
두개 이상의 컨텍스트가 명시되면 Context간 연결을 위한 컨테이너역할의 Context. * 명시란? - launch(Dispatchers.Main + job) 이렇게 코루틴 생성 시 프로퍼티를 설정한다는 의미
3) Element
Context의 각 요소(Element)들도 CoroutineConext를 구현한다고 위에서 언급한 의미
Coroutine Disaptchers
viewmodelScope.launch(Dispachers.IO).launch {
// working method
}
CoroutineExceptionHandler
suspend fum main() {
val exceptionHandler = CoroutineExceptionHandler { _, exception ->
println("CoroutineExceptionHandler : $exception")
}
val job = CoroutineScope(Dispachers.IO).launch(exceptionHandler) {
thorw IllegalArgumentException()
}
delay(1000)
}
suspend fum main() {
val exceptionHandler = CoroutineExceptionHandler { _, exception ->
println("CoroutineExceptionHandler : $exception")
}
val coroutineContext = Disapchers.IO + exceptionHandler
val job = CoroutineScope(coroutineContext) {
thorw IllegalArgumentException()
}
delay(1000)
}
Coroutine SupervisorJob
suspend fum main() {
val supervisor = SupervisorJob()
CoroutineScope(Dispachers.IO).launch(exceptionHandler) {
val firstChildJob = launch(Dispatchers.IO = supervisor) {
thorw AssertionError("첫 째 Job이 AsertionError로 인해 취소")
}
val secondChildJob = launch(Dispachres.Default) {
delay(1000)
println("둘 때 Job이 살아있습니다.")
}
firstChildJob.join()
secondChildJob .join()
}.join()
}
https://medium.com/hongbeomi-dev/코틀린의-코루틴-4-coroutine-context-and-dispatchers-1eab8f175428
https://jaejong.tistory.com/62
https://www.notion.so/Coroutine-2-177a5b30a7234cd79445c542a658e4b8