💻 프로그래밍

[🔥Coroutine] # 1 Coroutine을 배워봅시다

콩드로이드 2022. 8. 30. 14:37

 

안녕하세요:)  원래 비동기처리는 RxJava를 사용하고 있었는데,  Coroutine은 사용해본적이 없어서 배워볼까 합니다 

 

🧐 왜 Coroutine을 공부하는가?

안드로이드 메인스레드는 1개로  UI를 업데이트 하기 위해 사용되고, 별도로 멈추는 작업이 없다면 16ms마다 UI를 업데이트를 합니다 

그런데 만약 이 때 시간이 걸리는 DB 작업, 네트워크 작업을 하게 된다면 크래시가 발생하기 쉽습니다

그럼 이런 의문이 생기죠  메인 스레드를 멈추고 작업을 하면 되는거 아냐? 🤷‍♀️

하지만, main Thread를 너무 오래 멈추게 되면 ANR이 발생합니다 

main Thread를 크래시 없이 사용하기 위해선,  UI 이외의 작업은 별도의 스레드에서 해야합니다  

멀고도 험한 비동기의 길..

 

보통 콜백을 통해서 이런 위험성을 피했는데, 콜백은 '콜백지옥'에 갇히기 쉽습니다, 코드 파악도 어려워지구요

이 때 콜백 대신 사용할 수 있는게 RxJavaCoroutine입니다 

 

RxJava, Coroutine 둘 다 비동기를 처리하는데 쓰이는데 

RxJava는 배우지 않으면 코드를 이해하기 힘들 정도로 러닝커브가 높습니다 🥲

Coroutine이 RxJava에 비하면 러닝커브가 훨씬 낮습니다 

러닝커브뿐만 아니라 강남언니 블로그를 확인해보시면 성능에도 차이가 있음을 알 수 있습니다

또, Kotlin 사용시  Coroutine을 사용권장하므로 배워두면 좋을 거 같아요 

 


 

Coroutine을 사용하기 위해선 적어도 아래의 키워드들을 공부해야하는데, 하나씩 알아보겠습니다

Coroutine 키워드
coroutinecontext,  coroutine scope, coroutine builder, suspend, launch, async, delay, runBlocking, job, channel, dispatcher 

공식문서 , 차차님 블로그 참고했습니다

 


 

우선 아래의 라이브러리를 추가해줍니다 

dependencies {
  ...
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:x.x.x"
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:x.x.x"
}

 


🔥 Coroutine 

- 일시중지(suspend), 재개가 가능

- 어떤 스레드에도 종속되지 않음 -> 독립적으로 실행

- 비동기를 처리하는 더 단순화된 코드

 

val scope = CoroutineScope(CoroutineContext)
val job = scope.CoroutineBuilder {
...
}

 

 

📍CoroutineContext

- 코루틴이 실행될 쓰레드 풀을 지정 (Job 혹은 *Dispatcher)

  • Dispatchers.Main - 메인 쓰레드로 UI 작업, 그러므로  빠른 작업만 실행
    ex) Android UI 프레임워크 작업을 실행하며 LiveData 객체를 업데이트
  • Dispatchers.IO -  DataBase, 네트워크의 데이터 입출력 작업 처리
    ex)  Room 사용 시
  • Dispatchers.Default - CPU를 많이 사용하는 작업 실행
    ex) 목록 정렬, JSON 파싱 

 

 

📍 CoroutineScope

- 코루틴의 범위 지정

- Global Scope : CoroutineScope의 종류로 Application 생명주기에 종속

- 하지만, 공식문서에서 Global Scope의 사용은 권장되지 않습니다 

  💬 코루틴의 이점인 동시성을 없애며, 테스트를 어렵게 하기 때문입니다 (더 자세한 사항은 여길 확인해주세요)

   

 

 

📍 CoroutineBuilder

- 코루틴을 생성 및 실행을 담당합니다

 

 

1️⃣  launch

・  *job을 반환합니다

・  인자로 Dispatcher(코루틴이 실행될 쓰레드 지정) 사용할 수도 있습니다

 

* job

- 코루틴의 상태를 가지고 있으며, 코루틴을 제어할 수 있습니다 

 Job의 상태 

 

Job의 Method

• start : job을 실행하고, 현재 코루틴이 동작중이면 true, 준비 ・ 완료면 false를 반환합니다.

              해당 메소드는 suspend없이 동작하므로 코루틴 스코프 밖에서도 사용가능합니다

• join : job을 실행, 실행한 job이 끝날때까지 join을 호출한 코루틴을 일시중단(suspend)시킵니다

• cancel : job을 취소, 해당 메소드는 suspend없이 동작하므로 코루틴 스코프 밖에서도 사용가능합니다

• cancelAndJoin : job 종료를 요청 후, 종료가 될 때까지 기다립니다

• cancelChildren : 현재 scope내에 작성된 코루틴 중 부모를 제외한, children coroutine만 종료시킵니다

 

 

 

2️⃣  async

・  결과값 반환합니다 (launch와 차이)

・  *await()를 통해 결과값을 나타낼 수 있습니다

* await()

- 코루틴의 결과가 반환될 때까지 기다립니다 (suspend) -> 코루틴 내부 혹은 suspend함수 안에서만 사용 가능

 

 


🔥 suspend

suspend

 

1.  functionA 실행
2. functionA 일시 중지 (suspend) ,  functionB 실행 
3. functionB 완료
4. functionA 재개 

 

- 코루틴 내에서 일반 함수는 호출이 불가능 (일시 중지됐다가 재개됐다가 하기 때문에) -> suspend로 선언된 함수는 호출 가능합니다

- suspend로 선언된 함수내에서 다른 코루틴도 같이 동작 가능 합니다

GlobalScope.launch {
    doSomething()
    Log.d(TAG, "done something")
}

private suspend fun doSomething() {
    GlobalScope.launch {
        sleep(1000)
        Log.d(TAG, "do something in a suspend method")
    }
}

/**
출력결과
done something
do something in a suspend method
**/

 

 

runBlocking

• runBlocking 내의 코드가 완료되기 전까지 mainThread를 종료시키지 않습니다 (일시중지 및 재개 불가능)

• suspend를 쓸 수 없게 되는것이므로, 사용이 권장되진 않습니다 

 

fun main() = runBlocking { // this: CoroutineScope
    launch { // launch a new coroutine and continue
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println("World!") // print after delay
    }
    println("Hello") // main coroutine continues while a previous one is delayed
}


/**
출력결과
Hello
World!
**/

 

 


처음 코루틴을 공부하면서 적는 포스팅이다보니, 오류가 있을수도 있습니다 

궁금하신 점이나 의견이 있으시면 댓글 부탁드립니다 감사합니다 😊