본 게시글의 Coroutine 개념은 Android 내에서 사용되는 것을 전제로 작성되었다.
#1 ViewModel 속 전통적인 방식의 Coroutines
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
class SampleViewModel : ViewModel() {
private val myJob = Job()
private val myScope = CoroutineScope(Dispatchers.IO + myJob)
fun sampleFunction() {
myScope.launch {
// 아무 코드
}
}
override fun onCleared() {
super.onCleared()
myJob.cancel()
}
}
위는 ViewModel에서 Coroutine을 사용하는 코드다. ViewModel이 종료되었음에도, ViewModel에서 생성된 Coroutine은 계속 실행될 여지가 있다. 따라서, onCleared() 콜백 함수를 Override해서 Coroutine을 안전하게 종료시켰다.
하지만, 이 코드는 상용구 코드(Boilerplate code)의 전형이다. Job 프로퍼티와 onCleared() 함수의 존재는 Project의 크기가 작을 땐 몰라도 커지면 커질수록, ViewModel의 갯수도 많아지면서 골칫덩어리가 된다.
#2 lifecycle-viewmodel-ktx 라이브러리
#2-1 개요
다행히, #1의 문제를 해결할 수 있는 라이브러리가 있다. 바로, lifecycle-viewmodel-ktx다. 이 라이브러리의 ViewModelScope를 이용해서 #1 속 상용구 코드의 동작을 암시적으로 수행할 수 있다. 즉, 해당 상용구 코드를 제거해도 무방하게 만들 수 있다.
#2-1 androidx.lifecycle.ViewModel.kt 살펴보기 (ViewModelScope와 viewModelScope)
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.lifecycle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import java.io.Closeable
import kotlin.coroutines.CoroutineContext
private const val JOB_KEY = "androidx.lifecycle.ViewModelCoroutineScope.JOB_KEY"
/**
* [CoroutineScope] tied to this [ViewModel].
* This scope will be canceled when ViewModel will be cleared, i.e [ViewModel.onCleared] is called
*
* This scope is bound to
* [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate]
*/
public val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(
JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
)
}
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
ViewModelScope는 ViewModel의 수명주기를 참조해, ViewModel이 소멸될 때 알아서 종료되는 CoroutineScope다. #3-1에서처럼 라이브러리를 불러온 순간부터 ViewModelScope는 ViewModel의 프로퍼티로서 자동 생성된다. 해당 프로퍼티의 이름은 첫 글자가 소문자인, viewModelScope다. viewModelScope는 coroutineScope와 비슷하게 사용하면 된다.
#3 ViewModelScope 사용하기
#3-1 build.gradle.kts (Module)에서 라이브러리 다운로드
plugins {
...
}
android {
...
}
dependencies {
...
// ViewModelScope
val lifecycle_version = "2.6.2"
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")
...
}
여기에 있는 구문을 복사하여 모듈 수준 build.gradle에 붙여넣는다. 이제부터 ViewModelScope(viewModelScope)를 사용할 수 있다.
#3-2 ViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
class sampleViewModel : ViewModel() {
fun sampleFunction() {
viewModelScope.launch {
// 아무 코드
}
}
}
#1의 코드를 수정한다.
#4 요약
ViewModel에서의 CoroutineScope 생성 및 Cancellation을 암시화한다.
'깨알 개념 > Kotlin' 카테고리의 다른 글
[Kotlin] Coroutines - 한 Scope 내에서의 계층 관계 (0) | 2024.07.31 |
---|---|
[Kotlin] 위임 프로퍼티 (Delegated properties) (0) | 2024.07.22 |
[Kotlin] Coroutines - 스레드 전환 (0) | 2024.02.19 |
[Kotlin] Coroutines - Parallel Decomposition (0) | 2024.02.17 |
[Kotlin] Coroutines - Structured Concurrency (0) | 2024.02.16 |