LiveData with Coroutines and Flow — Part II: Launching coroutines with Architecture Components(학습 및 번역)
아키텍처 구성 요소로 코루틴 시작
Jetpack의 아키텍처 구성 요소는 많은 shortcut를 제공하므로 작업 및 취소에 대해 걱정할 필요가 없습니다.
opeations 범위를 선택하기만 하면 됩니다.
ViewModel scope
대부분의 데이터 opeations이 ViewModel에서 시작되기 때문에 코루틴을 시작하는 가장 일반적인 방법 중 하나입니다.
viewModelScope extention을 사용하면,
ViewModel이 지워지면 작업이 자동으로 취소됩니다.
viewModelScope.launch를 사용하여 코루틴을 시작합니다.
<!-- Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class MainActivityViewModel : ViewModel {
init {
viewModelScope.launch {
// Do things!
}
}
}
Activity and Fragment scopes
마찬가지로, lifecycleScope.launch를 사용하는 경우 View의 특정 인스턴스로 opeations 범위를 지정할 수 있습니다.
launchWhenResumed, launchWhenStarted 또는 launchWhenCreated를 사용하여 opeations을 특정 수명 주기 상태로 제한하면 범위를 더 좁힐 수도 있습니다.
<!-- Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class MyActivity : Activity {
override fun onCreate(state: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
// Run
}
lifecycleScope.launchWhenResumed {
// Run
}
}
}
Application scope
an application-wide scope에 대한 좋은 사용 예시가 있지만(https://medium.com/androiddevelopers/coroutines-patterns-for-work-that-shouldnt-be-cancelled-e26c40f142ad)
먼저 job을 결국 실행해야 하는 경우 WorkManager 사용을 고려해야 합니다.
ViewModel + LiveData
지금까지 우리는 코루틴을 시작하는 방법을 보았지만 결과를 받는 방법은 알지 못했습니다.
다음과 같이 MutableLiveData를 사용할 수 있습니다.
<!-- Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
// Don't do this. Use liveData instead.
class MyViewModel : ViewModel() {
private val _result = MutableLiveData<String>()
val result: LiveData<String> = _result
init {
viewModelScope.launch {
val computationResult = doComputation()
_result.value = computationResult
}
}
}
결과를 View에 노출할 것이기 때문에 코루틴을 시작하고, 불변 타입 LiveData를 통해 결과를 노출할 수 있게 하는 liveData 코루틴 빌더를 사용하여 일부 입력을 저장할 수 있습니다.
<!-- Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class MyViewModel : ViewModel() {
val result = liveData {
emit(doComputation())
}
}
emit()을 사용하여 업데이트된 데이터를 보냅니다.
LiveData Coroutine builder with a switchMap
LiveData의 값이 변경될 때마다 코루틴을 시작하려고 합니다.
예를 들어 데이터 로드 작업을 시작하기 전에 ID가 필요한 경우, Transformations.switchMap을 사용하는 편리한 패턴이 있습니다.
<!-- Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
private val itemId = MutableLiveData<String>()
val result = itemId.switchMap {
liveData { emit(fetchItem(it)) }
}
result는 itemId에 새로운 값이 있을 때마다 fetchItem suspend 함수를 호출한 결과로 업데이트되는 불변타입 LiveData입니다.
Emit all items from another LiveData
자주 사용되는 것은 아니지만, 일부 상용구를 절약할 수도 있습니다.
<!-- Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
liveData(Dispatchers.IO) {
emit(LOADING_STRING)
emitSource(dataSource.fetchWeather())
}
LiveData 소스를 전달하는 emitSource를 사용할 수 있습니다.
초기 값을 먼저 내보내고,나중에 값을 연속적으로 내보내려는 경우에 유용합니다.
Cancelling coroutines
위의 패턴 중 하나를 사용하면 작업을 명시적으로 취소할 필요가 없습니다.
그러나 기억해야 할 중요한 사항이 있습니다. 코루틴 취소는 cooperative입니다.
즉, 호출 코루틴이 취소된 경우 Kotlin이 작업을 중지하도록 도와야 합니다.
무한 루프를 수행하는 suspend 함수가 있다고 가정해 보겠습니다.
Kotlin은 해당 루프를 중지할 방법이 없으므로 작업이 정기적으로 isActive되어 있는지 확인하고 협력적 취소를 해야 합니다.
isActive 속성을 확인하여 이를 수행할 수 있습니다
<!-- Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
suspend fun printPrimes() {
while(isActive) {
// Compute
}
}
그런데 kotlinx.coroutines(예: delay)의 함수중 하나를 사용하는 경우 해당 기능이 모두 취소 가능하다는 것을 알아야 합니다.
<!-- Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
suspend fun printPrimes() {
while(true) { // Ok-ish because we call delay inside
// Compute
delay(1000)
}
}
즉, 나중에 해당 delay call을 제거하여 코드에 미묘한 버그가 발생할 수 있으므로 상관없이 isActive 검사를 추가하는 것이 좋습니다.
One-shot vs multiple values
코루틴(및 이에 대한 반응형 UI)을 이해하려면 다음을 중요하게 구분해야 합니다.
One-shot operations
-한 번 실행되고 결과를 반환받는 작업.
Operations that return multiple values:
- 시간이 지남에 따라 여러 값을 방출할 수 있는 데이터 소스에 대한 구독

One-shot operations with coroutines

suspend 함수를 사용하고 이를 viewModelScope 또는 liveData로 호출하는 것은 non-blocking 작업을 실행하는 매우 편리한 방법입니다.
<!-- Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class MyViewModel {
val result = liveData {
emit(repository.fetchData())
}
}
그러나 변경 사항을 관찰하고(듣고) 있으면 상황이 조금 더 복잡해집니다.
Receiving multiple values with LiveData

Kotlin의 Flow를 사용하는 것이 더 나은 접근 방식입니다.
flow는 RxJava 내의 reactive stream기능과 유사합니다.
그러나 코루틴이 non-blocking 원샷 작업을 훨씬 쉽게 만들지만 Flow의 경우에는 그렇지 않습니다.
stream은 여전히 파악하기 어렵고, 학습에 있어서 시간이 많이 필요합니다.
빠르고 견고한 반응형 UI를 만들고 싶다면 시간 투자 가치가 있다고 말하고 싶습니다.
flow는 언어의 일부이고, 적은 종속성이므로 많은 라이브러리에서 Flow 지원(예: Room)을 추가하기 시작했습니다.
LiveData 대신 Data Source 및 Repository의 Flow를 노출할 수 있지만,
ViewModel은 수명 주기를 인식하기 때문에 여전히 LiveData를 노출합니다.
