[Android][compose] Coroutines in Kotlin

hy3.nji·2023년 5월 29일
8

Jetpack Compose 🎀

목록 보기
1/6
post-thumbnail

Overview

Concurrency

앱에서 동시에 여러 일을 수행하는 것을 의미한다. 예시로는 웹 서버에서 데이터를 가져오는 것과 동시에 이용자의 입력을 받아 UI를 업데이트하는 일을 동시에 수행하는 것을 들 수 있다.
Kotlin에서는 Coroutine을 사용하여 Concurrency를 적용시킨다. Coroutine은 코드가 멈췄다가 나중에 실행되도록 하며, 이 사이에 다른 코드를 실행할 수 있다. Coroutine을 사용하면 asynchronous코드를 작성하기가 쉽다.

Synchronous code

Synchronous Code

한 번에 한 개의 작업만 수행하는 코드. 한 작업이 끝나야 다른 작업을 시작할 수 있다.
또한 synchronous code는 작업이 완전히 끝나야만 return 한다.

fun main() {
	println("Weather forecast")
    println("Sunny")
}

Suspending functions - (1)

Suspending function은 작업을 멈췄다가 재개할 수 있는 함수이다.
Suspending function은 suspending 함수나 coroutine 내부에만 작성이 가능하다.

// Suspend function 'delay' should be called
// only from a coroutine or another suspend function

import kotlinx.coroutines.*

fun main() {
    println("Weather forecast")
    delay(1000)
    println("Sunny")
}
import kotlinx.coroutines.*

fun main() {
	runBlocking {
    	println("Weather forecast")
        delay(1000)
        println("Sunny")
    }
}

runBlocking() 도 synchronous 함수이다. 내부에 작성된 모든 작업이 끝나야만 return하기 때문이다.
하지만 동시에 coroutine이다. delay(1000)이 실행된 시점에서 coroutine은 연기되고 다른 작업을 수행할 수 있기 때문이다. 1000초가 지나면 coroutine이 다시 재개된다.
runBlocking() 함수는 안에서 coroutineScope, launch, async 등과 같은 코루틴 함수들을 작성하는 것에 의미가 있다.

Suspending function - (2)

Suspend 함수는 suspension point를 가진다.

import kotlinx.coroutines.*

fun main() {
    runBlocking {
        println("Weather forecast")
        printForecast()
        printTemperature()
    }
}

suspend fun printForecast() {
    delay(1000)
    println("Sunny")
}

suspend fun printTemperature() {
	delay(1000)
    println("30\u00b0C")
}

runBlocking()

expect fun <T> runBlocking(
    context: CoroutineContext = EmptyCoroutineContext, 
    block: suspend CoroutineScope.() -> T
): T

delay()

suspend fun delay(timeMillis: Long)

Asynchronous code

import kotlinx.coroutines.*

fun main() {
    runBlocking {
        println("Weather forecast")
        launch {
            printForecast()
        }
        launch {
            printTemperature()
        }
    }
}

suspend fun printForecast() {
    delay(1000)
    println("Sunny")
}

suspend fun printTemperature() {
    delay(1000)
    println("30\u00b0C")
} 

launch()

새로운 코루틴을 만들어 작업을 동시에 수행하기 위해 필요한 함수이다.
launch{ printForecast() }printForecast()의 작업이 완료되기 전에 반환된다.
이와 같이 반환이 되었다고 해서 작업이 완료된 것이 아닌 함수를 비동기 함수라고 한다.

structured concurrency

Kotlin에서의 coroutine이 따르는 개념이다. kotlin 코드는 기본적으로 순차적이거나 기존의 event loop와 협력한다. 명시적으로 동시 실행을 요청했을 때(launch()를 사용한 경우)만 동시적으로 실행된다. 그런 경우가 아니라면 함수에서 제어 흐름이 반환될 때 함수 내부의 모든 작업이 끝나야한다.

import kotlinx.coroutines.*

fun main() {
    runBlocking {
        println("Weather forecast")
        launch {
            printForecast()
        }
        launch {
            printTemperature()
        }
        println("Have a good day!")
    }
}

suspend fun printForecast() {
    delay(1000)
    println("Sunny")
}

suspend fun printTemperature() {
    delay(1000)
    println("30\u00b0C")
} 

// Weather forecast
// Have a good day!
// Sunny
// 30°C

async()

코루틴이 끝나고 값을 받아와야 할 때 이용한다.
async() 함수는 Deffered 타입의 객체를 반환한다. Deffered 타입은 결과값이 준비되면 객체가 있다는 것을 보장한다. Deffered 타입 객체의 결과값에는 await() 함수를 통해 접근할 수 있다.

import kotlinx.coroutines.*

fun main() {
    runBlocking {
        println("Weather forecast")
        val forecast: Deferred<String> = async {
            getForecast()
        }
        val temperature: Deferred<String> = async {
            getTemperature()
        }
        println("${forecast.await()} ${temperature.await()}")
        println("Have a good day!")
    }
}

suspend fun getForecast(): String {
    delay(1000)
    return "Sunny"
}

suspend fun getTemperature(): String {
    delay(1000)
    return "30\u00b0C"
}

// 출력
// Weather forecast
// Sunny 30°C
// Have a good day!

Parallel Decomposition

문제를 병렬적으로 처리될 수 있는 작은 작업으로 쪼개는 것을 뜻한다.

import kotlinx.coroutines.*

fun main() {
    runBlocking {
        println("Weather forecast")
        println( getWeatherReport() )
        println("Have a good day!")
    }
}

suspend fun getWeatherReport() = coroutineScope {
    val forecast = async { getForecast() }
    val temperature = async { getTemperature() }
    "${forecast.await()} ${temperature.await()}"
}

suspend fun getForecast(): String {
    delay(1000)
    return "Sunny"
}

suspend fun getTemperature(): String {
    delay(1000)
    return "30\u00b0C"
}

coroutineScope()는 모든 작업이 끝나고 한 번만 return한다.
내부적으로 함수는 동시에 작업을 하지만, 호출자인 main()함수에서는 synchronous 작업으로 보인다.

Reference

Introduction to Coroutines in Kotlin Playground | Android Developer

profile
Android Developer

3개의 댓글

comment-user-thumbnail
2023년 5월 29일

앱 공부는 현지님 블로그 보면서 해야겠어요..

1개의 답글
comment-user-thumbnail
2023년 6월 1일

너무 잘 정리되어 있네요!!! 감사합니다:)

답글 달기