본문 바로가기

Kotlin

[Kotlin]Coroutine - 백그라운드에서의 실행

디벨러퍼 공식문서를 바탕으로 코루틴으로 백그라운드 작업을 실행시키는 방법을 알아보겠습니다.

 

#Coroutine - 백그라운드 작업

Developer Instruction to coroutines - https://developer.android.com/kotlin/coroutines?hl=en

서버에 로그인을 요청하는 Developer 사이트의 예제입니다

class LoginViewModel(
    private val loginRepository: LoginRepository
): ViewModel() {

    fun login(username: String, token: String) {
        // Create a new coroutine to move the execution off the UI thread
        viewModelScope.launch(Dispatchers.IO) {
            val jsonBody = "{ username: \"$username\", token: \"$token\"}"
            loginRepository.makeLoginRequest(jsonBody)
        }
    }
}
  1. ViewModel을 사용하고, viewModelScope을 Scope으로 지정해 코루틴을 생성/사용하고 있습니다
    • viewModelScope은 해당 뷰모델 destroy시 실행중인 모든 코루틴이 취소 되므로 뷰모델이 활성화 상태인 경우에만 실행해야 할 작업이 있을 때 유용 합니다
  2. login 함수는 Main Thread에서 실행 될 것이고 makeLoginRequest 함수는 네트워크 작업이므로 백그라운드에서 실행시켜야 합니다.
    • launch 함수 실행시 Dispatcher.IO를 지정해 I/O 작업 전용으로 예약된 스레드에서 실행되게끔 합니다
    • Dispatcher 지정으로 코루틴 실행에 사용되는 스레드를 정할 수 있습니다.

*Dispatcher?

더보기

Dispatcher?

  • 코루틴은 Dispatcher를 보고 코루틴이 실행될 Thread를 결정
  • 코루틴은 자체적으로 suspend 될 수 있고,  Dispatcher 는 다시 resume을 담당
  • Dispatcher 종류는 3가
    1. Dispatcher.Main : Main Thread에서 코루틴이 실행 됨
      • UI 작업이나 짧은 작업을 실행하기 위해서만 사용
    2. Dispatchers.IO :  Worker Thread에서 코루틴이 실행 되며, 이 Thread는 Disk작업이나 Network I/O 작업에 최적화 되어 있다
      • DB 접근, 파일 쓰기, 네트워크 작업 등에 사용
    3. Dispatchers.Default : Worker Thread에서 코루틴이 실행 되며, 이 Thread는 CPU 사용량이 많은 작업에 최적화되어 있다
      • 리스트 정렬, JSON 파싱 등에 사용

 

 

이 예제에서 makeLogoinRequest()는 Worker Thread에서 실행됨을 가정하고 있습니다.

즉 makeLogoinRequest를 호출 하는 쪽에서는 이 함수를 사용할 때 Worker Thread에서 동작하게끔 신경을 써주어야 합니다(Dispatcher.IO를 지정하는 등의 방법으로) 

혹시나 사용하는 쪽에서 실수로 Main Thread에서 makeLogoinRequest를 호출했다면 이는 Main Thread를 Blocking 하는 사태를 발생시킵니다. 고로 이런 함수는 main-safe 하지 않습니다

그렇다면 makeLogoinRequest() 함수를 main-safe한 함수로 변경해 봅시다.

 

#Coroutine - 백그라운드 작업 with main-safe function

main-safe한 makeLogoinRequest() 함수 입니다

class LoginRepository(...) {
    ...
    suspend fun makeLoginRequest(
        jsonBody: String
    ): Result<LoginResponse> {

        // Move the execution of the coroutine to the I/O dispatcher
        return withContext(Dispatchers.IO) {           
            // 네트워크 코드 작성 (I/O Thread 내부에서 동작)
        }
    }
}
  • withContext는 코루틴 내에서 사용되며 코루틴 코드 일부를 지정한 Dispatcher에 해당하는 Thread에서 실행되게끔 합니다. 즉, 다른 컨텍스트로 코루틴을 전환 합니다.
  • 네트워크 관련 코드를 withContext의 람다로 넘기고 Dispatchers.IO 지정을 통해 I/O Thread에서 동작하게 됨으로, 이 예제에서의 makeLogoinRequest 함수는 Main Thread에서 호출 하더라도(가정),  네트워크 작업이 Worker Thread에서 동작이 보장 되는 main-safe 함수입니다  ->  호출하는 입장에서 함수가 실행될 Thread를 생각할 필요가 X

그럼 main-safe 한 makeLogoinRequest() 함수를 호출하는 LoginViewModel의 login() 함수를 다시 보겠습니다

class LoginViewModel(
    private val loginRepository: LoginRepository
): ViewModel() {

    fun login(username: String, token: String) {

        // Create a new coroutine on the UI thread
        viewModelScope.launch {
            val jsonBody = "{ username: \"$username\", token: \"$token\"}"

            // Make the network call and suspend execution until it finishes
            val result = loginRepository.makeLoginRequest(jsonBody)

            // Display result of the network request to the user
            when (result) {
                is Result.Success<LoginResponse> -> // Happy path
                else -> // Show error in UI
            }
        }
    }
}
  • makeLoginRequest가 suspend 함수이므로 호출하기 위해서 여전히 코루틴을 하나 만들어야 합니다(suspend 키워드를 붙이면 해당 함수가 코루틴 내부에서 호출되도록 강제됩니다)
  • viewModelScop에서 실행된 코루틴은 Dispatchers를 미지정시 기본으로 'Dispatchers.Main.immediate'이 셋팅됩니다.
  • lanuch로 생성된 코루틴은 Main Thread에서 실행되게 되고, makeLogoinRequest() 함수는 Main Thread에서 호출 됩니다(main-safe 하게!)
  • makeLogoinRequest()는 suspend 함수 이므로  함수 동작이 완료 될 때 까지 코루틴을 일시 정지 합니다
  • makeLogoinRequest() 네트워크 작업이 완료되면(withContext 블록이 완료되면) 작업 완료 결과와 함께 Main Thread에서 실행이 다시 Resume 되고, 성공 or 실패 UI작업도 할 수 있게 됐습니다.
  • 이처럼 순차적으로 코드를 나열 되어 있어서 네트워크 작업이 Main Thread를 Block 하는 것 처럼 보일 수 있지만 Main Thread를 Block 하지 않습니다

 


 

*Developer Instruction to coroutines - https://developer.android.com/kotlin/coroutines?hl=en

 

*Main-safety

https://eyegochild.medium.com/kotlin-coroutine-2-main-safety%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B0%80%EB%8A%A5%ED%95%9C-%EA%B1%B8%EA%B9%8C-91ccb6cd2f7a

 

잘못된 내용이나 궁금한 내용 있으시면 댓글 달아주세요

좋은 하루 되세요!