[Android] 코루틴이랑 친해지기

moKo·2022년 4월 19일
0

Android

목록 보기
8/13
post-thumbnail

1. Coroutine의 구성요소

  • 코루틴의 생성
val scope = CoroutineScope(Dispatchers.IO)    // CoroutineScope 생성
val job = scope.launch{    // 아래 코드를 방금 만들어준 CoroutineScope에서 실행
    delay(1000)
    println("Work is done!")
}
  1. Dispatchers.IO를 이용하여 Coroutine이 실행될 CoroutineScope를 만들고
  2. 만들어준 CoroutineScope에서 launch를 이용하여 { } 안의 코드를 Coroutine으로 실행시킨다.
  • 코루틴의 세 가지 구성요소와 흐름
    Context로 Scope를 만들고, Builder를 이용하여 그 Scope 안에서 실행

1-1. 첫 번째 구성요소 - CoroutineContext

첫번째 구성요소로 CoroutineContext는 말 그대로 코루틴이 실행될 Context, 즉 맥락을 지정해주는 것이라고 하는데, 예를 쉽게 들어주셔서 이해하기 편했다.

올림픽 경기를 예시로 들면서 설명하시길, 각 선수들은 정해진 종목들이 있을 것이고, 그 종목에 맞는 경기장에서 경기를 치뤄야 한다.

이를 통해 이해하면, 만들어줄 코루틴을 선수라고 정하고, CoroutineContext를 경기장으로 생각하면 된다.

CoroutineContext를 설정해준다는 것은 Coroutine의 실행 목적에 맞게 실행될 특정 Thread Pool을 지정해주는 것이라고 이해하면 편하다.

Dispatcher.Main : UI를 구성하는 작업이 모여있는 쓰레드 풀

Dispatcher.IO : (파일 혹은 소켓을) 읽고 쓰는 작업이 모여있는 쓰레드 풀

Dispatcher.Default : 기본 쓰레드 풀, CPU 사용량이 많은 작업에 적합

1-2. 두 번째 구성요소 - CoroutineScope

CoroutineContext로 Coroutine이 어디서 실행될지를 정해주었다면 이 Coroutine을 제어할 수 있는 Scope, ‘범위’를 지정해주어야 한다.

이때 말하는 제어라는 것은 작업을 취소시키거나, 어떤 작업이 끝날 때까지 기다리는 것을 의미한다.

  • CoroutineScope의 종류 두 가지
  1. 사용자 지정 CoroutineScope
val scope = CoroutineScope(CoroutineContext ex. Dispatchers.Main...)
val job = scope.launch{
// TODO
}

가장 기본적인 방식으로 특정 코루틴이 필요해질 때마다 새로 선언해주고, 필요 없어지면 종료되도록 할 수 있다.

  1. GlobalScope
public object GlobalScope : CoroutineScope {
/**
* Returns [EmptyCoroutineContext].
*/
override val coroutineContext: CoroutineContext
get() = EmptyCoroutineContext
}

GlobalScope.launch{
// TODO
}

CoroutineScope의 특별한 형태로, 앱이 실행될 때부터 앱이 종료될 때까지 코루틴을 실행시킬 수 있는 스코프이다.

어떤 activity에서 globalscope를 통해 실행된 코루틴은 activity가 종료되어도 해당 코루틴의 완료시까지 동작한다.

앱이 실행되는 동안 장시간, 혹은 주기적으로 실행되야 하는 코루틴에 적합하며, 필요할 때만 수행되어야 하는 코루틴은 사용자 지정 CoroutineScope사용이 권장된다.

1-3. 세 번째 구성요소 - CoroutineBuilder

CoroutineBuilder는 설정해준 context와 scope를 통해 코루틴을 실행시키는 함수이다.

종류로는 launch{}, async{} 가 있으며 coroutinescope의 확장함수로써 {} 내부의 코드를 코루틴으로 실행시키는 역할을 한다.

위 두 함수는 동일한 기능을 하지만 서로 다른 객체를 반환한다.

launch로 실행된 코루틴은 Job객체를 반환하고, async로 실행된 코루틴은 Deferred객체를 반환한다.

val job = scope.launch {    // Job
    // launch TODO
}
 
val deferred = scope.async {    // Deferred
    // async TODO
}

2. Android에서 Coroutine적용하기

class MainActivity : AppCompatActivity(), CoroutineScope {
    lateinit var job: Job
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Default + job
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        job = Job()
 
        launch {
            // Coroutine
        }
    }
 
    override fun onDestroy() {
        super.onDestroy()
        job.cancel()    // Activity 종료 시 진행 중인 Coroutine 취소
    }
}

안드로이드에 코루틴을 적용한 코드이다.

CoroutineScope 인터페이스를 상속받아 Context를 설정해지고 onDestroy()에 job.cancel()을 통해 액티비티 종료 시, 진행중인 모든 코루틴을 취소하게 되어있다.

위와 같이 Coroutine을 사용하기 위한 기본 설정이 끝났다면 onCreate, onResume과 같은 함수에서 CoroutineScope 선언 없이 바로 launch{} 혹은 async{}를 이용하여 Coroutine을 실행시킬 수 있다.

이 때 onCreate 함수 자체는 CoroutineBuilder의 { } 내부가 아니므로 job.join()과 같은 suspend 함수를 바로 사용할 수는 없음에 주의해야 한다.


내용 및 사진 출처, 이해를 도와준 분들 🙏 ( 클릭 시 이동됩니다 )

profile
🔥 Feelings fade, results remain

0개의 댓글