ANR 문제란?
코루틴을 알아보기 전에 ANR을 먼저 알아보도록 하자.
ANR은 액티비티가 응답하지 않는 오류 상황을 의미한다. 액티비티를 작성할 때 ANR을 고려하지 않으면 앱이 수시로 종료될 수 있다.
액티비티가 사용자 이벤트에 5초 이내에 반응하지 않으면 ANR 오류가 발생한다.
시스템에서 액티비티를 실행하는 수행 흐름을 메인 스레드 또는 화면을 출력하는 수행 흐름이라는 의미에서 UI 스레드라고 한다.
ANR 문제를 해결하는 방법은 액티비티를 실행한 메인 스레드 이외에 실행 흐름을 따로 만들어서 시간이 오래 걸리는 작업을 담당하게 하면 된다.
그런데 이 방법으로 대처하면 ANR 오류는 해결되지만 화면을 변경할 수 없다는 다른 문제가 생기게 된다.
왜냐하면 개발자가 만든 스레드에서는 화면 변경을 할 수 없고, 액티비티를 출력한 메인 스레드에서만 할 수 있기 때문이다.
코루틴(Coroutione)이란?
액티비티의 ANR 오류를 해결하는 방법으로 코루틴이 있다.
코루틴은 일종의 경량 스레드로, 여러 개의 코루틴이 아니라 스레드에서 동작할 수 있는 기능이다.
비동기 경량 스레드라고 요약할 수 있다.
코루틴의 장점은 다음과 같다.
Android 프로젝트에서 코루틴을 사용하려면 앱의 build.gradle 파일에 다음 종속 항목을 추가해야 한다.
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
화면에서 버튼을 클릭하면 아래의 코드처럼 오래 걸리는 작업이 진행된 후 결괏값이 버튼 아래의 텍스트 뷰에 출력되는 예를 가지고 설명해보도록 하겠다.
val channel = Channel<Int>()
val backgroundScope = CoroutineScope(Dispatchers.Default + Job())
backgroundScope.launch {
var sum = 0L
var time = measureTimeMillis {
for (i in 1..2_000_000_000) {
sum += i
}
}
Log.d("leee", "time : $time")
channel.send(sum.toInt())
}
val mainScope = GlobalScope.launch(Dispatchers.Main) {
channel.consumeEach {
binding.resultView.text = "sum : $it"
}
}
위에서는 backgroundScope와 mainScope를 만들었다. 여기서 주목할 점은 스코프를 만들면서 지정한 디스패처이다. 디스패처는 이 스코프에서 코루틴이 어디에서 동작해야 하는지를 나타낸다.
즉, 위의 코드는 시간이 오래 걸리는 작업을 Dispatchers.Default로 지정한 스코프에서 구동한 코루틴이 처리하고, 그 결과를 Dispatchers.Main으로 지정한 스코프의 코루틴에서 화면에 출력하도록 작성한 것이다.
또한 위에서는 Channel을 이용했는데 이 클래스는 코루틴의 값을 전달받을 수 있는 방법을 제공한다.