Developing Myself Everyday
article thumbnail

 

이 게시글의 내용은 아래의 유튜브를 보고 작성한 내용입니다.

유튜브의 22분부터 마지막까지의 내용을 다루고 있습니다.

 


 

 

코루틴의 Context?


코루틴의 Context에 말해보기에 앞서 Context에 대해 다시한번 말해보자면 안드로이드 애플리케이션의 실행 환경에 대한 전역 정보를 제공하는 클래스라고 할 수 있습니다.

 

그럼 코루틴의 Context은 바로 코루틴 환경에 대한 전역 정보를 제공하고 있다는 것을 우리는 알 수 있습니다.

 

 

 

코루틴에 Context가 생긴 이유

사실 초기의 코루틴에서는 Context가 없었습니다. 그런데 문제가 생겼습니다. 바로 코루틴이 실행될 Thread에 관련된 사항입니다.

작가 starline 출처 Freepik

 

 

❓ 질문을 하나 해보겠습니다. 아래와 같은 Continuation이 발생할 때 `createPost(token, item)`이 어떤 Thread에서 재개되는지 대답하실 수 있으신가요??

 

정답은 "`createPost(token, item)`의 정확한 세부 구현 사항에 달려있다" 입니다. 

 

그렇기에 때문에 외부에서는 우리가 해당 함수가 어떤 Thread에서 재개될지 지정할 수 없었습니다. 이를 가능하게 하는 것이 바로 코루틴 Context입니다.

 

그래서 현재의 Continuation에는 아래와 같이 Context가 있습니다.

public interface Continuation<in T> {
    public val context: CoroutineContext
    public fun resumeWith(result: Result<T>)
}

 

 

 

코루틴 Context의 사용

코루틴 Context가 어떻게 사용되는지 보여드리기 위해 코루틴 빌더 launch를 가져와 보겠습니다.

fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext, 
    start: CoroutineStart = CoroutineStart.DEFAULT, 
    block: suspend CoroutineScope.() -> Unit
): Job

 

launch는 위처럼 Context, 코루틴 실행 옵션, 코루틴의 본문을 정의하는 Block이 있습니다. 코루틴 빌더를 사용해 코루틴을 시작할 때, 아래와 같이 Context를 지정해주면 UI 스레드에서 작업이 수행됩니다.

 

 

 

그런데 이게 어떻게 가능할까요?

 

 

바로 ContinuationInterceptor라는 interface를 통해 가능해졌습니다. 

public interface ContinuationInterceptor : CoroutineContext.Element {

    public companion object Key : CoroutineContext.Key<ContinuationInterceptor>

    public abstract fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T>
}

 

ContinuationInterceptor는 Context의 일부로 Context에서 ContinuationInterceptor 인스턴스를 처리하는데 사용할 수 있는 Key와 Continuation의 Wrapper 역할을 하는 interceptContinuation 메서드가 있습니다.

 

interceptContinuation 메서드가 있음으로 우리가 원하는 대로 다룰 수 있는 사용자 정의 Wrapper를 정의할 수 있습니다.


 

 

아래의 그림을 보며 다시한번 동작을 이해해 보겠습니다.

 

 

1. 코루틴은 특정한 CoroutineScope에서 실행되었습니다. 해당 코루틴 스코프가 갖는 Context에서 DispatcherUI Thread에서 작업을 수행하기 위해 UI Dispatcher라고 되어있습니다.

 

2. 특정 CoroutineScope 안에서 또 다른 코루틴을 만들었습니다. 해당 코루틴은 ThreadPollDispather를 이용해 백그라운드 스레드에서 수행됩니다.

 

3. 이러한 Continuation이 실행되었을 경우 변경된 코드 블럭은 최초에 SUSPEND 상태로 생성되었다가 resume() 요청으로 인해 RESUMED 상태로 전환되어 실행됩니다. 

 

4. resume()이 요청될 때마다 현재 ContextDispather에게 스레드 전환이 필요한지 확인한 후 전환이 필요하다면 dispatch() 함수를 호출해 적합한 스레드로 전달합니다.

 

 

 

Job에서의 코루틴 Context의 사용

launch 빌더로 코루틴을 실행하면 job  얻을 수 있습니다. 

 

job을 사용하면 코루틴을 join 하거나 cancel 할 수 있습니다.

val job = launch { ... }
job.join()
job.cancel()

 

job의 내부를 좀 더 자세히 볼까요??

interface Job : CoroutineContext.Element {
  companion object Key : CoroutineContext.Key<Job>
  ...
}

 

흠.. 어디서 본 것 같지 않으신가요??

 

맞습니다 job 또한 ContinuationInterceptor처럼 코루틴 Context의 요소로 존재합니다.

 

이것은 우리가 코루틴 안에 있다는 것을 의미하며 이를 지속적인 Context로 가져가서 job을 검색하고 취소할 수 있는 것입니다.

launch {
  val job = coroutineContext[Job]!!
  val interceptor = coroutineContext[CoroutineInterceptor]!!
}
profile

Developing Myself Everyday

@배준형

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!