Android Coroutine 사용하기
1. Coroutine
Coroutine은
Co(협력) + Routine(규칙적인 작업)의 합성어로
하나의 작업이 끝날 때까지 계속 진행되는 것이 아니라
실행 중간에 다른 작업을 하러 갔다가 다시 돌아와서 작업을 이어서 진행할 수 있다.
1.1. Coroutine 은 Thread 가 아니다.
같은 백그라운드 작업을 하는 점에서 비슷하지만
Coroutine은 하나의 작업이라면
Thread는 그 작업을 수행하는 공간이다.
즉 하나의 Thread에서 여러 Coroutine 을 동시에 실행할 수 있다.
1.2. 의존성 추가
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5"
1.3. Scope
코루틴은 실행 범위, 제어 범위를 지정할 수 있으며 이 범위를 Coroutine Scpoe 라고 한다.
GlobalScpoe : 프로그램 어디서나 제어, 동작이 가능한 범위
CoroutineScpoe :특정한 목적을 지정하여 제어 및 동작이 가능한 범위
CoroutineScope(Dispatchers.IO)
CoroutineScope(Dispatchers.Main)
CoroutineScope(Dispatchers.Default)
IO : 네트워크 작업이나 DB에 접근하는 등 백그라운드에서 필요한 작업을 수행
Main: 메인스레드 작업으로 UI 갱신이나, Toast 등 View 관련 작업 수행
Default : 무거운 연산 작업 수행
2. launch 와 async
코루틴 반환의 여부에 따라 사용된다
2.1. launch
launch는 반환이 없는 job 객체를 반환한다.
launch {
for (i in 1..5){
println(i)
delay(10)
}
}
해당 코루틴의 결과를 대기하려면 join 을사용한다.
val a = launch {
for (i in 1..5){
println(i)
delay(10)
}
}
a.join()
2.2. async
async는 반환값이 있는 Deffered 객체를 반환한다.
val b = async {
for (i in 1..5){
println(i)
delay(10)
}
"async 종료"
}
해당 코루틴의 결과를 대기하려면 await 을사용한다.
val b = async {
for (i in 1..5){
println(i)
delay(10)
}
"async 종료"
}
b.await()
2.3. runBlocking
fun main(){
val scope = GlobalScope
scope.launch {
for (i in 1..5){
println(i)
}
}
}
위와 같이 코드를 작성 한 경우 화면에는 출력이 되지 않는다.
그 이유는 코루틴은 스코프 혹은 프로그램이 종료가 되면 같이 종료가 되기 때문에
main 이 종료가 되면서 같이 종료가 된 것이다.
코루틴이 종료될 때까지 메인루틴을 대기하면 화면에 출력된다.
fun main(){
runBlocking {
launch {
for (i in 1..5){
println(i)
}
}
}
}
이경우 안드로이드에서 메인스레드 를 오랫동안 대기하면 ANR이 발생하니 주의를 해야 한다.
3. 여러 Dispatchers 의 처리
3.1. 비동기 메서드 생성
suspend fun callApi(): String {
return "OK"
}
함수 앞에 붙은 suspend는 이 함수가 비동기, 즉 코루틴 안에서 실행하도록 하는 것이다.
만약 코루틴이 아닌 곳에서 사용하려 하면 경고를 출력한다.
3.2. 여러 Dispatchers
CoroutineScope(Dispatchers.IO).launch {
val result = callApi()
textView.text = result
}
위와 같이 백그라운드 작업과 UI 작업이 동시에 일어날 때
두 종류의 작업을 처리해야 하는 경우가 생긴다.
물론
CoroutineScope(Dispatchers.IO).launch {
val result = callApi()
CoroutineScope(Dispatchers.Main).launch {
textView.text = result
}
}
위와 같이 코루틴 안에 또 코루틴을 생성하여 처리할 수 있지만
이럴 때 사용하는 withContext가 있다.
CoroutineScope(Dispatchers.IO).launch {
val result = callApi()
withContext(Dispatchers.Main){
textView.text = result
}
}
4. 네트워크 타임아웃 처리
네트워크 타임아웃 처리는 withTimeoutOrNull로 쉽게 처리할 수 있다.
시간이 경과하면 null 을 반환한다.
CoroutineScope(Dispatchers.IO).launch {
val result = withTimeoutOrNull(10000) {
callApi()
}
if (result != null) {
withContext(Dispatchers.Main){
textView.text = result
}
}
}