본문 바로가기
개발공부/Kotlin

[Kotlin] 코루틴 Coroutine - async와 await, LifecycleScope과 ViewModelScope 사용법 및 사용예제- 4편

by 익스플로어 2021. 6. 14.
반응형

 

[Kotlin] 코루틴 Coroutine  - async와 await, LifecycleScope과 ViewModelScope 사용법 및 사용예제- 4편

 

저번 포스트에서는 coroutine에서 join, repeat, cancel, withTimeout을 사용하는 법을 배웠습니다.  이번 포스트에서는 Async와 await을 활용하여 여러 작업을 동시에 하고 값을 접근하는 법을 배우도록 하겠습니다.  그리고 GlobalScope 말고도 lifeCycleScope , ViewModelScope을 이용한 예제를 다뤄보도록 하겠습니다. 

 

이전 포스트: 

 

[Kotlin] 코루틴 Coroutine 제어하기 -Join, repeat,cancel,withTimeout - 3편

[Kotlin] 코 루틴 Coroutine 사용법 및 개념 정리 -Join, repeat, Cancel,withTimeout - 3편 전 포스트에서는 runblocking을 다뤄보았는데요 join, repeat을 쓰기 위해서 runBlocking을 아셔야 하기 때문에 모르신..

underdog11.tistory.com

 

Async 필요한 이유

  • 예시로 설명하겠습니다. 우선 아래코드처럼 두 개의 네트워크 call을 실행해야 하고 각 테스크당 3초씩 걸립니다.
  • measureTimeMillis안에 함수들을 넣어주면 처리하는데 시간을 볼수있습니다. 그리고 그 시간을 로그로 보겠습니다.
class MainActivity : AppCompatActivity() {

    val TAG = "MainActivity"

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

        GlobalScope.launch(Dispatchers.IO) {
            val time = measureTimeMillis {
                val answer1 = networkCall() //3초 딜레이
                val answer2 = networkCall2()// 3초딜레이
                Log.d(TAG, "Answer1 is $answer1")
                Log.d(TAG, "Answer2 is $answer2")
            }
            Log.d(TAG,"Requests took $time ms.") //0 6초
        }

        suspend fun networkCall(): String{
            delay(3000L)
            return "Answer 1"
        }

        suspend fun networkCall2():String{
            delay(3000L)
            return "Answer 2"
        }
    }
}
  • 로그를 확인해보면 6초가 걸립니다. 그 이유는 networkCall()이 끝나야 networkcall2()가 진행되기 때문입니다. 둘이 동시에 진행하려면 저번 포스트에서 했던 join을 이용하여 동시에 진행할 수 있습니다. 
GlobalScope.launch(Dispatchers.IO) {

//변경시작
            val time = measureTimeMillis {
                var answer1:String? = null
                var answer2:String? = null
                val job1 = launch { answer1 = networkCall() }//3초후 networkCall()의 return값인 Answer1을 string으로 리턴합니다.
                val job2 = launch { answer2 = networkCall2() }

                job1.join()
                job2.join()
                //job1과 job2가 동시에 실행됩니다.
//변경끝
                Log.d(TAG, "Answer1 is $answer1")
                Log.d(TAG, "Answer2 is $answer2")
            }
            Log.d(TAG,"Requests took $time ms.")
            //job1과 job2가 동시에 실행됬기때문에 3만에 모든 작업이 끝나게됩니다. 
        }
        suspend fun networkCall(): String{
            delay(3000L)
            return "Answer 1"
        }

        suspend fun networkCall2():String{
            delay(3000L)
            return "Answer 2"
        }

 

이렇게 join을 쓰는것보다 더 간편하게 코드를 쓰는 방법이 있습니다. 동시에 작업을 원한다면  Async를 이용해 더 쉬운 코드를 작성하실수 있습니다. 

Async, await활용

  • Async는 새로운 coroutine을 시작하고 GlobalScope.launch과 비슷하지만 ,  GlobalScope.launch처럼 job(백그라운드 작업)을 리턴하지않고 Deferred를 리턴합니다. 

CoroutineScope.launch는 job을 리턴합니다/ CoroutineScope.async는 deferred를 리턴합니다.

  • Async를 활용한 예시입니다.
GlobalScope.launch(Dispatchers.IO) {

//변경시작
            val time = measureTimeMillis {
                val answer1 = async { networkCall() }
                val answer2 = async { networkCall2() }
                Log.d(TAG, "Answer1 is ${answer1.await()}")
                Log.d(TAG, "Answer2 is ${answer2.await()}")
            }
            Log.d(TAG, "Request took $time ms.")
        }
//변경후
        suspend fun networkCall(): String {
            delay(3000L)
            return "Answer 1"
        }

        suspend fun networkCall2(): String {
            delay(3000L)
            return "Answer 2"
        }
  • 위에서 join을 활용했을때보다 훨씬 코드가 간결해진 것을 보실 수 있습니다. 
  • 그리고 answer값들을 로그로 확인할때 await을 사용해주었습니다. 
  • Async를 사용했기때문에 deferred값을 리턴합니다. deferred값을 리턴할 때는 await을 사용해줘야 합니다. await은 스레드를 방해하지 않고 deferred값이 계산될 때까지, 기다리게 하는 함수입니다.

 

LifecycleScope

GlobalScope은 topLevel 코루틴 이고 앱 라이프사이클중 항상 살아있습니다. LifecycleScope은 현재 라이프 사이클에서만 살아있는 코루틴입니다. 코드로 비교해서 보여드리겠습니다.

 

  • GlobalScope
class MainActivity : AppCompatActivity() {

    val TAG = "MainActivity"

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

        btnStartActivity.setOnClickListener{
            GlobalScope.launch {
                while (true){ //button눌린게 true면 still running로그 반복 프린트
                    delay(1000L) //1초 간격으로 printing
                    Log.d(TAG,"Still running")

                }
                //when new intent was created,
            }
            GlobalScope.launch {
                delay(5000L) //5초후 새로운 intent시작
                Intent(this@MainActivity, SecondActivity::class.java).also{
                    startActivity(it)
                    finish()
                }
            }
        }
    }

}
  • Log를 확인해보면 Globalscope안에있는 while이 버튼이 눌리면 1초마다 Still running이 반복되고,  5초후 새로운 intent가 열리게 됩니다. 새로운 인텐트가 열린 후에도 while문이 멈추지않는것을 볼수있습니다.

  • lifecycleScope
class MainActivity : AppCompatActivity() {

    val TAG = "MainActivity"

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

        btnStartActivity.setOnClickListener{
            //변경 시작
            lifecycleScope.launch {
            //변경 끝
                while (true){ //button눌린게 true면 still running로그 반복 프린트
                    delay(1000L) //1초 간격으로 printing
                    Log.d(TAG,"Still running")

                }
                //when new intent was created,
            }
            GlobalScope.launch {
                delay(5000L) //5초후 intent시작
                Intent(this@MainActivity, SecondActivity::class.java).also{
                    startActivity(it)
                    finish()
                }
            }
        }
    }

}
  • 위코드를 실행하면Intent로 activity가 바뀌는 시점인 5초후 while문이 멈추는것을 볼수있습니다.

ViewModelScope

ViewModelScope의 사용방식은 lifescycleScope, GlobalScope과 같습니다. ViewModelScope은 Viewmodel이 살아있는 동안 진행하는 코드입니다.

class MyViewModel: ViewModel() {
    init {
        viewModelScope.launch {
            // Coroutine that will be canceled when the ViewModel is cleared.
        }
    }
}

참고 자료:

 

아키텍처 구성요소로 Kotlin 코루틴 사용  |  Android 개발자  |  Android Developers

Kotlin 코루틴은 비동기 코드를 작성할 수 있게 하는 API를 제공합니다. Kotlin 코루틴을 사용하면 코루틴이 실행되어야 하는 시기를 관리하는 데 도움이 되는 CoroutineScope를 정의할 수 있습니다. 각

developer.android.com

 

 

Composing suspending functions | Kotlin

 

kotlinlang.org


 

댓글0