코틀린 기반으로 쓰여짐
1) What is?
Android의 Kotlin 코루틴 | Android 개발자 | Android Developers
Android의 Kotlin 코루틴 | Android 개발자 | Android Developers
Android의 Kotlin 코루틴 코루틴은 비동기적으로 실행되는 코드를 간소화하기 위해 Android에서 사용할 수 있는 동시 실행 설계 패턴입니다. 코루틴은 Kotlin 버전 1.3에 추가되었으며 다른 언어에서 확
developer.android.com
안드로이드 디밸로퍼에 따르면 코루틴이란 비동기적으로 실행되는 코드를 간소화하기 위한 동시 실행 설계 패턴이다.
그래서 그게 뭔데?
코루틴은 CO(협동,같이하다)+ROUTINE(루틴)의 합성어다
코루틴의 가장 큰 특징이라함은 출구와 입구가 여러개라는 것이다.
일반적인 서브루틴의 경우에는 중단이라는 의미가 존재하지 않는다 그렇지만 코루틴은 가능하다.
또 구글 코드랩은 아래와 같이 서술한다.
즉 코루틴의 핵심은, 비동기적 콜백을 순차적 코드로 변환해준다는것이다.
**여기서 잠깐,비동기 출력이란?
특정 로직의 실행이 끝날때까지 기다려주지 않고 나머지 코드를 먼저 실행하는것을 비동기 처리라고 한다.
2) 실전예제
우선 나는 코루틴의 ㅋ 도 모르기 때문에
https://kotlinlang.org/docs/coroutines-basics.html#your-first-coroutine
Coroutines basics | Kotlin
kotlinlang.org
도움을 받아서 그대로 따라할것이다.
1) 코루틴 만들기
package com.example.coroutine
import kotlinx.coroutines.*
fun main() = runBlocking { // this: CoroutineScope
launch { // launch a new coroutine and continue
delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
println("World!") // print after delay
}
println("Hello") // main coroutine continues while a previous one is delayed
}
코루틴을 공부하고 있는 입장이라면 아 출력값이 WORLD! HELLO 일리는 없겠구나 하고 대충 예상할 수 있지만
어쨌든간에 돌려보면
잘 나온다.
여기서 각각 요소들의 역할은 뭔가?
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
// --------------- async ---------------
사실 뭔지 잘 모르겠지만 return 값 자체가 coroutine이니까 코루틴을 만들어주는거구나. 정도로 이해했다.
launch is a coroutine builder. It launches a new coroutine concurrently with the rest of the code, which continues to work independently. That's why Hello has been printed first.
delay is a special suspending function. It suspends the coroutine for a specific time. Suspending a coroutine does not block the underlying thread, but allows other coroutines to run and use the underlying thread for their code.
(코루틴을 특정 시간동안 연기 시켜준다. 코루틴의 연기(Suspending)는 근본적인 쓰레드를 차단하지는 않지만, 다른 코루틴들의 동작을 허락하고 근본적인 쓰레드를 그들의 코드에 이용한다. )
runBlocking is also a coroutine builder that bridges the non-coroutine world of a regular fun main() and the code with coroutines inside of runBlocking { ... } curly braces. This is highlighted in an IDE by this: CoroutineScope hint right after the runBlocking opening curly brace.
If you remove or forget runBlocking in this code, you'll get an error on the launch call, since launch is declared only in the CoroutineScope:
Coroutines can be thought of as light-weight threads
코루틴은 경량 쓰레드로 생각할 수 있다. 이에 관하여 예제를 살펴보자면
fun main() = runBlocking {
repeat(100_000) { // launch a lot of coroutines
launch {
delay(5000L)
print(".")
}
}
}
컴퓨터 상태마다 다르겠지만 하나도 버벅임이 없다.
thread로 비교한번 해보자면
fun main() = runBlocking {
repeat(100_000) { // launch a lot of coroutines
thread {
Thread.sleep(5000L)
print(".")
}
}
}
확실히 차이가 있다!
fun main() = runBlocking { // this: CoroutineScope
GlobalScope.launch { // launch a new coroutine and continue
delay(3000L) // non-blocking delay for 3 second (default time unit is ms)
println("World!") // print after delay
}
println("Hello") // main coroutine continues while a previous one is delayed
delay(2000L)
}
}
만약 위와 같은 코드가 있다면 어떤 결과를 낳을까?
결과값은 아마도 Hello! 만 뜰것이다
왜냐면 코루틴 자체가 3초뒤에 결과값을 출력하고 싶은데 전체가 2초뒤면 끝나기 때문이다.
그럼 얘를 어떤식으로 해결 할 수 있을까?
launch를 하게되면 job을 반환하게 되는데
fun main() = runBlocking { // this: CoroutineScope
val job =GlobalScope.launch { // launch a new coroutine and continue
delay(3000L) // non-blocking delay for 3 second (default time unit is ms)
println("World!") // print after delay
}
println("Hello") // main coroutine continues while a previous one is delayed
job.join()
}
위와 같은 결과값을 나타낸다. join은 코루틴이 완료 된 후에 메인함수가 종료 되게끔 만들어준다.
근데 위와같은 방법은 문제가 많다. 가장 대표적으로는 앞으로 수많은 코루틴을 쓰게 될텐데
그때마다
val job = glbalscope~~~
val2 job = globalscop~~
이런식으로 관리를 해야하는데, 이는 굉장히 번거롭고 귀찮은 방법이다.
그렇다면 조금 더 세련된 방법은 없을까? 이에 관해 구글은 structured concurrency 라는것을 제공한다
structured concurrency 란?
Coroutines follow a principle of structured concurrency which means that new coroutines can be only launched in a specific CoroutineScope which delimits the lifetime of the coroutine. The above example shows that runBlocking establishes the corresponding scope and that is why the previous example waits until World! is printed after a second's delay and only then exits.
그래서 어떻게하는데? 방법은 간단하다!
fun main() = runBlocking { // this: CoroutineScope
this.launch {
delay(2000L)
println("WORLD!")
}
this.launch {
delay(1000L)
println("World!")
}
println("Hello!,")
}
크게 달라진건 없다! 근데 join이 없는데도 결과값은 잘 나온다!
그렇다면 핵심은?
TOP-LEVEL에 코루틴을 만들지 말고 코루틴의 자식들에 코루틴을 만들어라
왜?? 부모 코루틴이 자식 코루틴이 완료 될때까지 기다려주기 때문에 관리하기가 훨씬 수월하다!
이렇게 진행하다보니 특정 함수를 추출해서 따로 관리하고 싶어졌다 그래서
fun main() = runBlocking { // this: CoroutineScope
launch {
myworld()
}
println("Hello!,")
}
fun myworld() {
delay(1000L)
println("world.")
}
}
요런식으로 해보니
코루틴안에서나 서스펜드 함수안에서만 불려야 한다고 오류가 뜬다.
그래서 앞에 suspend 키워드를 붙여주니 잘된다.
fun main() = runBlocking { // this: CoroutineScope
launch {
myworld()
}
println("Hello!,")
}
suspend fun myworld() {
delay(1000L)
println("world.")
}
[Coroutine] 5. suspend fun의 이해
일시중단 가능한 코루틴 코루틴은 기본적으로 일시중단 가능하다. launch로 실행하든 async로 실행하든 내부에 해당 코루틴을 일시중단 해야하는 동작이 있으면 코루틴은 일시 중단된다. 예시로
kotlinworld.com
관련해서 매우매우 좋은 포스팅이 있어 올린다.
참고
'프로그래밍 > 안드로이드' 카테고리의 다른 글
싱글톤 패턴 - 안드로이드 (0) | 2022.09.27 |
---|---|
프레그먼트와 네비게이션 바 - 안드로이드 (0) | 2022.09.21 |
제트팩1 - 안드로이드 (0) | 2022.09.06 |
Modern Android Development - 안드로이드 (0) | 2022.09.06 |
리사이클러뷰 - 안드로이드 (0) | 2022.08.22 |