반응형
[Kotlin] 코루틴 Coroutine - async와 await, LifecycleScope과 ViewModelScope 사용법 및 사용예제- 4편
저번 포스트에서는 coroutine에서 join, repeat, cancel, withTimeout을 사용하는 법을 배웠습니다. 이번 포스트에서는 Async와 await을 활용하여 여러 작업을 동시에 하고 값을 접근하는 법을 배우도록 하겠습니다. 그리고 GlobalScope 말고도 lifeCycleScope , ViewModelScope을 이용한 예제를 다뤄보도록 하겠습니다.
이전 포스트:
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를 리턴합니다.
- 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.
}
}
}
참고 자료:
반응형