π± dreaming DiNO
[Kotlin] μλλ‘μ΄λ μ½λ£¨ν΄ κΈ°λ³Έ κ°λ κ³Ό νμ© λ³Έλ¬Έ
μ½λ£¨ν΄
μ½λ£¨ν΄(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