Notice
Recent Posts
Recent Comments
Link
Β«   2024/10   Β»
일 μ›” ν™” 수 λͺ© 금 ν† 
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

🌱 dreaming DiNO

[Kotlin] μ•ˆλ“œλ‘œμ΄λ“œ 코루틴 κΈ°λ³Έ κ°œλ…κ³Ό ν™œμš© λ³Έλ¬Έ

Android

[Kotlin] μ•ˆλ“œλ‘œμ΄λ“œ 코루틴 κΈ°λ³Έ κ°œλ…κ³Ό ν™œμš©

MK_____ 2023. 6. 14. 18:08

코루틴

코루틴(Coroutines) 은 μ“°λ ˆλ“œ(Thread)와 κΈ°λŠ₯μ μœΌλ‘œλŠ” λΉ„μŠ·ν•˜μ§€λ§Œ, ν•˜λ‚˜μ˜ μ“°λ ˆλ“œ λ‚΄μ—μ„œ μ—¬λŸ¬ 개의 코루틴이 μ‹€ν–‰λ˜λŠ” κ°œλ…μœΌλ‘œ 비동기 ν”„λ‘œκ·Έλž˜λ°μ— ꢌμž₯λ˜λŠ” λ™μ‹œ μ‹€ν–‰ 섀계 νŒ¨ν„΄μž…λ‹ˆλ‹€.

코루틴은 λ‹¨μΌ μ“°λ ˆλ“œ λ‚΄μ—μ„œ μ—¬λŸ¬ 개의 코루틴을 μ‹€ν–‰ν•  수 있기 λ•Œλ¬Έμ—, λ§Žμ€ μ–‘μ˜ λ™μ‹œ μž‘μ—…μ„ μ²˜λ¦¬ν•  수 μžˆμœΌλ©΄μ„œ λ©”λͺ¨λ¦¬ μ ˆμ•½μ˜ μž₯점이 μžˆμŠ΅λ‹ˆλ‹€.

μ΄μœ λŠ”, κΈ°μ‘΄ μ“°λ ˆλ“œλŠ” Context-Switching(CPUκ°€ μ“°λ ˆλ“œλ₯Ό μ μœ ν•˜λ©΄μ„œ μ‹€ν–‰, μ’…λ£Œλ₯Ό λ°˜λ³΅ν•˜λ©° λ©”λͺ¨λ¦¬ μ†Œλͺ¨)이 λ°œμƒν•˜κΈ° λ•Œλ¬Έμ— λ§Žμ€ μ–‘μ˜ μ“°λ ˆλ“œλ₯Ό κ°–κΈ°κ°€ μ–΄λ ΅μ§€λ§Œ

λ°˜λ©΄μ— μ½”루틴은 μ“°λ ˆλ“œκ°€ μ•„λ‹Œ 루틴을 μΌμ‹œ 쀑단(suspend) ν•˜λŠ” 방식이라 Context-Switching에 λΉ„μš©μ΄ 듀지 μ•ŠκΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

λ˜ν•œ, μ§€μ •λœ μž‘μ—… λ²”μœ„ λ‚΄μ—μ„œ 싀행이 되기 λ•Œλ¬Έμ— λ©”λͺ¨λ¦¬ λˆ„μˆ˜λ₯Ό 방지할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

즉, μ½”루틴은 μ“°λ ˆλ“œμ˜ κ°„μ†Œν™”λœ 버전이라고 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

코루틴 이미지 도식화

μ£Όμš” κ°œλ…

코루틴에 λŒ€ν•΄ μ΄ν•΄ν•˜κΈ° μœ„ν•΄μ„œ 기본적으둜 μ•Œμ•„μ•Ό ν•˜λŠ” κ°œλ…μ€ μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

 

- CoroutineScope

- CoroutineContext (Job, Dispatchers)

- CoroutineBuilder (launch, async)

- susfend function

CoroutineScope

코루틴이 μ‹€ν–‰λ˜λŠ” λ²”μœ„λ‘œ, 코루틴을 μ‹€ν–‰ν•˜κ³  싢은 Lifecycle에 따라 μ›ν•˜λŠ” Scopeλ₯Ό μƒμ„±ν•˜μ—¬ 코루틴이 싀행될 μž‘μ—… λ²”μœ„λ₯Ό 지정할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

β–Ά μ‚¬μš©μž 지정 CoroutineScope : CoroutineScope(CoroutineContext)

    ex) CoroutineScope(Dispatchers.Main) // Dispatchers.Main, Dispatchers.Default, Dispatchers.IO, Job()...

// 메인 μ“°λ ˆλ“œμ—μ„œ 싀행될 μ‚¬μš©μž μ •μ˜ Scope
val scope = CoroutineScope(Dispatchers.Main)
scope.launch {
    // 메인 μ“°λ ˆλ“œ μž‘μ—…
}

// λ°±κ·ΈλΌμš΄λ“œμ—μ„œ 싀행될 μ‚¬μš©μž μ •μ˜ Scope
CoroutineScope(Dispatchers.IO).launch {
    // λ°±κ·ΈλΌμš΄λ“œ μž‘μ—…
}

 

β–Ά GlobalScope : 앱이 싀행될 λ•ŒλΆ€ν„° μ’…λ£Œλ  λ•ŒκΉŒμ§€ μ‹€ν–‰

// μ•±μ˜ λΌμ΄ν”„μ‚¬μ΄ν΄λ™μ•ˆ 싀행될 Scope
GlobalScope.launch {
    // λ°±κ·ΈλΌμš΄λ“œλ‘œ μ „ν™˜ν•˜μ—¬ μž‘μ—…
    launch(Dispatchers.IO) {

    }

    // λ©”μΈμ“°λ ˆλ“œλ‘œ μ „ν™˜ν•˜μ—¬ μž‘μ—…
    launch(Dispatchers.Main) {

    }
}


μ•ˆλ“œλ‘œμ΄λ“œ Jetpack 라이브러리(AAC)μ—μ„œλŠ” 코루틴을 μ‰½κ²Œ μ‚¬μš©ν•  수 μžˆλ„λ‘ 각 Lifecycle에 λ§žλŠ” Scopeλ₯Ό μ œκ³΅ν•΄μ£Όκ³  μžˆμŠ΅λ‹ˆλ‹€.

β–Ά ViewModelScope : ViewModel λŒ€μƒ, ViewModel이 제거되면 코루틴 μž‘μ—…μ΄ μžλ™μœΌλ‘œ μ·¨μ†Œλ©λ‹ˆλ‹€.

class MyViewModel: ViewModel() {
    init {
        viewModelScope.launch {
            // ViewModel이 제거되면 코루틴도 μžλ™μœΌλ‘œ μ·¨μ†Œλ©λ‹ˆλ‹€.
        }
    }
}


β–Ά LifecycleScope : Lifecycle 객체 λŒ€μƒ(Activity, Fragment, Service...), Lifecycle이 끝날 λ•Œ 코루틴 μž‘μ—…μ΄ μžλ™μœΌλ‘œ μ·¨μ†Œλ©λ‹ˆλ‹€.

class MyActivity : AppCompatActivity() {
    init {
        lifecycleScope.launch {
            // Lifecycle이 끝날 λ•Œ 코루틴 μž‘μ—…μ΄ μžλ™μœΌλ‘œ μ·¨μ†Œλ©λ‹ˆλ‹€.
        }
   }
}


β–Ά liveData : LiveData λŒ€μƒ, LiveDataκ°€ ν™œμ„±ν™”λ˜λ©΄ 싀행을 μ‹œμž‘ν•˜κ³  λΉ„ν™œμ„±ν™”λ˜λ©΄ μžλ™μœΌλ‘œ μ·¨μ†Œλ©λ‹ˆλ‹€.

val user: LiveData<User> = liveData {
    val data = repository.loadUser() // suspend function
    emit(data)
}

 

CoroutineContext

코루틴 μž‘μ—…μ„ μ–΄λ–€ μ“°λ ˆλ“œμ—μ„œ μ‹€ν–‰ν•  것인지에 λŒ€ν•œ λ™μž‘μ„ μ •μ˜ν•˜κ³  μ œμ–΄ν•˜λŠ” μš”μ†Œμž…λ‹ˆλ‹€.

μ£Όμš” μš”μ†Œλ‘œλŠ” Jobκ³Ό Dispatchersκ°€ μžˆμŠ΅λ‹ˆλ‹€.

 

β–Ά Job : 코루틴을 κ³ μœ ν•˜κ²Œ μ‹λ³„ν•˜κ³ , μ½”루틴을 μ œμ–΄ν•©λ‹ˆλ‹€. 

val job = CoroutineScope(Dispatchers.IO).launch {
    // 비동기 μž‘μ—…
}

job.join()      // μž‘μ—…μ΄ μ™„λ£Œλ˜κΈ°κΉŒμ§€ λŒ€κΈ°
job.cancel()    // μž‘μ—… μ·¨μ†Œ

 

val job1 = Job()
CoroutineScope(job1 + Dispatchers.Main).launch {
    // 메인 μ“°λ ˆλ“œ μž‘μ—…

    launch(Dispatchers.IO) {
        // 비동기 μž‘μ—…
    }

    withContext(Dispatchers.Default) {
        // 비동기 μž‘μ—…
    }
}

val job2 = CoroutineScope(Dispatchers.IO).launch {
    // 비동기 μž‘μ—…
}

job1.cancel() // job1이 μ—°κ²°λœ 코루틴 μž‘μ—… μ·¨μ†Œ

 

 

β–Ά Dispatchers : 코루틴을 μ–΄λ–€ μ“°λ ˆλ“œμ—μ„œ μ‹€ν–‰ν•  것인지에 λŒ€ν•œ λ™μž‘μ„ μ§€μ •ν•©λ‹ˆλ‹€.

Dispatchers.Main : μ•ˆλ“œλ‘œμ΄λ“œμ˜ 메인 μ“°λ ˆλ“œλ‘œ, UI μž‘μ—…μ„ μœ„ν•΄ μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.
예λ₯Ό λ“€μ–΄, UIλ₯Ό κ΅¬μ„±ν•˜κ±°λ‚˜ LiveDataλ₯Ό μ—…λ°μ΄νŠΈ ν•  λ•Œ μ‚¬μš©λ©λ‹ˆλ‹€.

Dispatchers.IO : λ„€νŠΈμ›Œν¬, λ””μŠ€ν¬ I/O 싀행에 μ΅œμ ν™”λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.
예λ₯Ό λ“€μ–΄, Retrofit으둜 λ„€νŠΈμ›Œν¬ 톡신을 ν•˜κ±°λ‚˜, Fileμ΄λ‚˜ Room λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 데이터λ₯Ό 읽고/μ“Έ λ•Œ μ‚¬μš©λ©λ‹ˆλ‹€. 

Dispatchers.Default : CPU μ‚¬μš©λŸ‰μ΄ λ§Žμ€ 무거운 μž‘μ—… μ²˜λ¦¬μ— μ΅œμ ν™” λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.
예λ₯Ό λ“€μ–΄, 데이터λ₯Ό κ°€κ³΅ν•˜κ±°λ‚˜ λ³΅μž‘ν•œ μ—°μ‚°, JSON νŒŒμ‹±μ„ ν•  λ•Œ 주둜 μ‚¬μš©λ©λ‹ˆλ‹€. 

CoroutineBuilder

μœ„μ—μ„œ μ„€μ •ν•œ CoroutineScope와 CoroutineContextλ₯Ό 톡해 λΉ„λ‘œμ†Œ! λ“œλ””μ–΄! μ½”루틴을 μ‹€ν–‰μ‹œμΌœμ£ΌλŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€.

μ£Όμš” μš”μ†Œλ‘œλŠ” launch와 asyncκ°€ μžˆμŠ΅λ‹ˆλ‹€.

 

β–Ά launch : Job 객체이며, 결과값을 λ°˜ν™˜ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

μ‹€ν–‰ ν›„ 결과값이 ν•„μš” μ—†λŠ” λͺ¨λ“  μž‘μ—…μ€ launchλ₯Ό μ‚¬μš©ν•˜μ—¬ μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

CoroutineScope(Dispatchers.Main).launch {
    // 결과값이 ν•„μš”μ—†λŠ” μž‘μ—…
}

 

β–Ά async : Deferred 객체이며, 결과값을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

await() ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬, 코루틴 μž‘μ—…μ˜ μ΅œμ’… 결과값을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

val deferred = CoroutineScope(Dispatchers.Main).async {
    // κ²°κ³Όκ°’
    "Hello Coroutine!"
}

val message = deferred.await() // await()ν•¨μˆ˜λ‘œ κ²°κ³Όκ°’ λ°˜ν™˜
println(message)

 

β–Ά withContext : async와 λ™μΌν•˜κ²Œ 결과값을 λ°˜ν™˜ν•˜λ©°, asyncμ™€μ˜ 차이점은 await()을 ν˜ΈμΆœν•  ν•„μš”κ°€ μ—†λ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.

async{ }.await() κ³Ό λ™μΌν•˜λ‹€κ³  보면 λ©λ‹ˆλ‹€.

코루틴 λ‚΄λΆ€λ‚˜ susfend ν•¨μˆ˜ μ•ˆμ—μ„œ κ΅¬ν˜„μ΄ κ°€λŠ₯ν•˜λ©°, 콜백이 ν•„μš” 없이 μ½”λ“œμ˜ μ“°λ ˆλ“œ 풀을 μ œμ–΄ν•  수 있기 λ•Œλ¬Έμ— λ„€νŠΈμ›Œν¬ μš”μ²­μ΄λ‚˜ DB 쑰회 같은 μž‘μ—…μ— 주둜 μ‚¬μš©ν•©λ‹ˆλ‹€.

init {
    viewModelScope.launch {       // Dispatchers.Main
        val user = getUserInfo()  // Dispatchers.Main
    }
}

suspend fun getUserInfo(): User =                   // Dispatchers.Main
        withContext(Dispatchers.IO) {               // Dispatchers.IO
            val response = apiService.getUserInfo() // Dispatchers.IO
            if (response.isSuccessful) {            // Dispatchers.IO
                return@withContext response.body()  // Dispatchers.IO
            } else {                                // Dispatchers.IO
                return@withContext null             // Dispatchers.IO
            }                                       // Dispatchers.IO
        }                                           // Dispatchers.Main

susfend function

코루틴 μ•ˆμ—μ„œλ§Œ μ‹€ν–‰ν•  수 μžˆλŠ” 코루틴 μ „μš© λ©”μ†Œλ“œμž…λ‹ˆλ‹€.

susfend λ©”μ†Œλ“œλŠ” 일반적인 κ³³μ—μ„œ ν˜ΈμΆœν•  수 μ—†μœΌλ©°, λ°˜λ“œμ‹œ 코루틴 μ•ˆμ—μ„œλ§Œ 호좜이 κ°€λŠ₯ν•©λ‹ˆλ‹€.

μ΄μœ λŠ” μ½”λ£¨ν‹΄μ˜ 싀행이 μΌμ‹œμ€‘λ‹¨(susfend) λ˜κ±°λ‚˜ λ‹€μ‹œ 재개(resume) 될 수 있기 λ•Œλ¬Έμ—, μ»΄νŒŒμΌλŸ¬μ—κ²Œ 이 λ©”μ†Œλ“œλŠ” μ½”루틴 μ•ˆμ—μ„œ μ‹€ν–‰ν•  λ©”μ†Œλ“œμž„μ„ μ •μ˜ν•˜κΈ° μœ„ν•΄ λ©”μ†Œλ“œλͺ… μ•žμ— "suspend" λ₯Ό λΆ™μ—¬μ€˜μ•Ό ν•©λ‹ˆλ‹€.

 

suspend fun getUser(): User {
    ...
    return user
}

 

코루틴을 μ‚¬μš©ν•˜μ‹€ 땐 μ„Έ 가지 μˆœμ„œλ§Œ κΈ°μ–΅ν•˜μ„Έμš”!

1. μ–΄λ–€ μ“°λ ˆλ“œμ—μ„œ μ‹€ν–‰ν•  것 인지 Dispatchers λ₯Ό μ •ν•˜κ³  (Dispatchers.Main, Dispatchers.IO, Dispatchers.Default)

2. 코루틴이 싀행될 Scopeλ₯Ό μ •ν•˜κ³  (CoroutineScope, ViewModelScope, LifecycleScope, liveData...)

3. launch λ˜λŠ” async둜 코루틴을 μ‹€ν–‰ μ‹œν‚€λ©΄ λ©λ‹ˆλ‹€!

Context둜 Scopeλ₯Ό λ§Œλ“€κ³ , Builderλ₯Ό μ΄μš©ν•˜μ—¬ 코루틴을 μ‹€ν–‰!

 

 

좜처: https://0391kjy.tistory.com/49