ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Android Coroutine 사용하기
    Android 2021. 9. 25. 17:49
    test

    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
    		}
    	}
    }