개발자이야기
kotlin coroutine 본문
kotlin coroutine
Android 어플리케이션은 Background Thread 를 만들어주지 않으면 Main Thread에서 모든일을 처리 한다는 것을 대부분 알고 계실 것 입니다. Main Thread는 이미 ui를 출력해주는 작업 만으로도 많은 일을 하고있는 셈 입니다.
만약 오랜 시간이 걸리는 작업을 Main Thread에서 처리한다면 ANR(Application Not Responding)을 만나게 될 것 입니다. 그래서 우리는 Main Thread의 부담을 덜어주기 위해 네트워크 작업, 시간이 오래 걸리는 작업 등을 Multi Thread 환경에서 작업합니다. 우리는 이러한 비동기 처리를 위해 android 에서 AsyncTask, RxJava를 사용하곤 했습니다.
코루틴이란?
코루틴은 가벼운 스레드 입니다. 경량 스레드(light-weight thread)라고도 표현합니다.
코루틴은 매우 가볍다는 것 입니다. 수백개의 코루틴을 만들어도 문제되지 않습니다.
(코루틴은 스레드와 비교되지만 병렬처리의 개념이 아닙니다! 이 내용에 대해서는 다음 포스팅으로 이어가겠습니다.)
코루틴의 중요한 개념을 간단히 살펴보겠습니다.
- suspend
- Dispatchers
- Job
- CoroutineScope
1. suspend
suspend 키워드는 컴파일러에게 이 함수가 코루틴 스코프에서 실행 되어야 한다는 것을 알려줍니다.
suspend 함수 내에서 순차적으로 실행하고 완료될 때까지 기다릴 수 있습니다.
suspend fun coroutineFunction() {
}
위에서 설명 하였지만 suspend 함수는 코루틴 스코프 또는 다른 suspend 함수에서만 호출되어야 합니다.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch {
println("case1=${System.currentTimeMillis()}")
val value = coroutineFunction()
println("case2=${System.currentTimeMillis()}")
println("case3=${System.currentTimeMillis()}value = $value")
}
}
}
suspend fun coroutineFunction(): Int {
println("case4=${System.currentTimeMillis()}")
delay(5000)
return 100
}
결과
I/System.out: case1=1629729538838
I/System.out: case4=1629729538841
I/System.out: case2=1629729543901
I/System.out: case3=1629729543911 value = 100
결과에서 알 수 있듯 suspend 함수 내에서 5초의 시간 delay를 넣어놓았고 마치 일반적인 동기화 프로세스처럼 작동하며 5초를 기다린 후 결과를 반환 하는 것을 알 수 있습니다.
2. Dispatchers
디스패처는 작업을 수행해야 하는 스레드를 지정합니다.
Dispatchers.Main (벌써부터 ui 업데이트에 사용될거 같은 냄새가 물씬 풍깁니다.)
Main Thread에서 코루틴을 실행합니다. 이 디스패처는 ui와 상호작용하고 빠른 작업을 할때만 사용해야 합니다.
Dispatchers.Default
대기시간이 없고 지속적인 cpu 작업이 요구되는 JSON파싱, 복잡한 연산 작업에 적당한 디스패처 입니다.
Dispatchers.IO
대기시간이 있는 네트워크 작업에 적합한 디스패처 입니다.
3. job
코루틴을 컨트롤 하기 위해 job을 제공해 줍니다. 우리는 job을 이용해 코루틴을 취소 할 수 있습니다.
class SubActivity: AppCompatActivity() {
val job = Job()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activty_sub)
CoroutineScope(Dispatchers.IO+job).launch {
delay(5000)
CoroutineScope(Dispatchers.Main).launch {
hello.text = "안뇽"
}
}
hello.setOnClickListener {
job.cancel()
}
}
}
위의 코드에서 변수 job 객체를 생성하였습니다. 그리고 생성하는 CoruoutineScope를 추가하여 컨트롤 할 수 있습니다.
위의 코드에서 자식 스코프인 CoroutineScope(Dispatchers.Main) 실행되기 전 job을 이용하여 취소하면 실행되지 않습니다. 하지만 만약 자식 스코프가 실행된 상태에선 어떻게 취소해야 할까요?
val job = Job()
CoroutineScope(Dispatchers.IO+job).launch {
CoroutineScope(Dispatchers.Main+job).launch {
repeat(5) {
delay(1000)
println("취소확인!")
}
}
}
job.cancel()
위처럼 자식 스코프에 디스패처 등록을 job과 같이 해주면 됩니다. 하나의 job 객체에 n개의 스코프를 등록해 줌으로써 함께 취소할 수 있습니다.
CoroutinScope.kt
@Suppress("FunctionName")
public fun CoroutineScope(context: CoroutineContext): CoroutineScope =
ContextScope(if (context[Job] != null) context else context + Job())
CoroutineScope 를 생성할때 job을 함께 등록해주면 위의 CoroutineScope 함수에 디스패처와 job이 인자값으로 넘어오며 함께 등록됩니다. 역시나 친절합니다.
Job 객체에는 cancel 메소드 이외에도 start, join, cancelAndJoin 등이 있습니다. 자세한 내용은 추후에 포스팅 하도록 하겠습니다.
4. CoroutineScope
CoroutineScope는 launch 또는 async를 사용하여 만든 코루틴을 추적합니다. 진행 중인 작업, 즉 실행 중인 코루틴은 언제든지 scope.cancel()을 호출하여 취소할 수 있습니다. Android에서 일부 KTX 라이브러리는 특정 수명 주기 클래스에 자체 CoroutineScope를 제공합니다. 예를 들어 ViewModel에는 viewModelScope가 있고 Lifecycle에는 lifecycleScope가 있습니다. 하지만 디스패처와 달리 CoroutineScope는 코루틴을 실행하지 않습니다. (공식사이트 인용)
간단히 launch 함수를 알아보겠습니다.
launch
launch() 함수는 Job 객체를 반환합니다.
val job = CoroutineScope(Dispatchers.IO).launch {
delay(5000)
}
job.join()
job.cancel()
반환받은 객체로 코루틴 블럭을 취소할 수 있으며 join 메소드로 코루틴 블럭이 완료 되기를 기다릴 수도 있습니다.
좀더 정확하게 쓰기위해 알아야 할 것들이 아직 많이 있습니다. 내용을 계속 추가 작성할 계획입니다.
틀린부분은 지적 부탁드립니다. 읽어주셔서 감사합니다.
'kotlin' 카테고리의 다른 글
kotlin data model 쉽게 만들기 (0) | 2021.08.02 |
---|---|
kotlin map, flatMap (0) | 2021.06.25 |
kotlin 고차함수 (1) | 2021.06.06 |
kotlin scope functions (let,run,with,apply,also) (0) | 2021.06.01 |