본문 바로가기

Language/Kotlin

Kotlin Coroutine Thread 변경하기, Coroutine Suspending Functions (중단 함수)

반응형

먼저 Coroutine이 작동하는 Thread를 변경하는 것에 대해서 다루겠습니다.

 

 

문제 상황

이전 Hello Couroutine! 에서는 코루틴을 사용해 백그라운드 스레드에서 로그를 남기도록 했습니다.

하지만 UI를 수정하는 작업을 백그라운드 스레드에서는 할 수 없어 UI 관련 작업은 할 수 없었습니다.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btnDownloadUserData.setOnClickListener {
            // 버튼 클릭시 백그라운드 스레드에서 코루틴 실행함
            CoroutineScope(Dispatchers.IO).launch {
                downloadUserData()
            }

        }
    }

    private fun downloadUserData() {
    	// 현재 스레드 이름 로그로 남기도록 함
        Log.i("MyTag", "Hello Coroutine! in ${Thread.currentThread().name}")
    }
}

 

해결책

이번에는 Coroutine Suspending 함수인 withContext()delay()를 통해 백그라운드 스레드에서 메인 스레드로 변경해 UI 관련 작업을 해보도록 하겠습니다.

withContext()를 사용할 때 코루틴 컨텍스트를 Dispatchers.Main으로 지정함으로써 메인 스레드에서 작업할 수 있도록 할 수 있습니다.

withContext()에 대한 자세한 설명은 해당 문서에 있습니다.

class MainActivity : AppCompatActivity() {
    private var count = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btnDownloadUserData.setOnClickListener {
            // 버튼 클릭시 백그라운드 스레드에서 코루틴 실행함
            CoroutineScope(Dispatchers.Main).launch {
                downloadUserData()
            }

        }
    }

    private suspend fun downloadUserData() {
        withContext(Dispatchers.Main) {
            for (i in 1..200000) {
                // 현재 스레드 이름과 텍스트 뷰에 보이도록 함 
                tvUserMessage.text = "Hello Coroutine! $i in ${Thread.currentThread().name}"
                delay(1)
            }
        }
    }
}

 

결과

버튼 클릭시 TextView에 메인 스레드 이름과 번호가 변경되는 것을 확인할 수 있습니다. 

 

Suspending Functions (중단 함수)

coroutine이 suspended 될 때 실행된 함수의 현재 stack frame은 복사되고 메모리에 저장됩니다.

tasks를 완수하고 함수가 resume되면 stack frame은 저장된 곳에서 가져오고 다시 시작합니다.

 

코틀린 코루틴 API의 suspending functions에는 다음과 같은 것이 있다.

  • withContext
  • withTimeout
  • withTimeoutOrNull
  • join
  • delay
  • await
  • supervisorScope
  • coroutineScope

Retrofit 또는 Room같은 라이브러리도 suspending 함수를 제공합니다.

 

suspending 함수를 사용한다면 호출 함수에 suspend를 표시해줘야 합니다.

private suspend fun downloadUserData() {
        withContext(Dispatchers.Main) {
            for (i in 1..200000) {
                // 현재 스레드 이름과 텍스트 뷰에 보이도록 함 
                tvUserMessage.text = "Hello Coroutine! $i in ${Thread.currentThread().name}"
                delay(1)
            }
        }
}

 

suspend modifier가 있는 suspend 함수는 코루틴 block에서만 호출이 가능합니다.

// 버튼 클릭시 백그라운드 스레드에서 코루틴 실행함
CoroutineScope(Dispatchers.Main).launch {
   downloadUserData()
}

또는 다른 suspending 함수에서만 호출이 가능합니다.

평범한 함수에서는 suspending 함수를 호출할 수 없습니다.

 

참고 : kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html

 

 
반응형