[Android(Kotlin)] 코루틴(Coroutine) 다시 공부하기 - 1

업데이트:

카테고리:

태그: , ,

오늘 GlobalScope와 ViewModelScope의 차이가 무엇이냐는 질문에 대답하지 못하였다. 그냥 ViewModel에서는 ViewModelScope를 사용하고 나머지는 GlobalScope를 사용하는 무식한 방식으로 코루틴을 사용해오면서 왜? 라는 의문조차 가지지 않았다는 사실에 조금 충격…(알고보니 썩 좋은 사용법도 아니었다.) 그래서 다시 처음부터 공부해보기로 했다.

kotlin 기초 - 3에서 이어지는 내용으로 오늘은 CoroutineScope 중점으로 작성을 해볼까 한다.


1. CoroutineScope


CoroutineScope는 코루틴의 scope를 정의하는 인터페이스로 코루틴 블록을 묶음으로 제어할 수 있는 단위이다. launch 또는 async를 사용해 만든 코루틴을 추적하고 언제든지 scope.cancel()를 통해 취소할 수 있게 한다.

1.1. GlobalScope

싱글톤으로 만들어진 CoroutineScope의 한 종류로 앱의 생명주기와 함께 동작하기 때문에 별도의 생명 주기 관리가 필요없다. 앱 실행부터 종료까지 긴시간동안 실행되는 코루틴의 경우에 사용하는 것이 적합하다. GlobalScope에 따르면 GlobalScope는 섬세하므로(생명주기를 따르므로 취소나 예외처리가 복잡하다.) 실수로 리소스 또는 메모리 누수가 발생하기 쉽고 구조적 동시성의 원칙을 따르지 않으므로 느린 네트워크 등의 문제로 중단되거나 지연되는 경우 계속 작동하며 리소스를 소비한다.

fun loadConfiguration() {
    GlobalScope.launch {
        val config = fetchConfigFromServer() // network request
        updateConfiguration(config)
    }
}

위의 함수를 호출하면 GlobalScope에서 코루틴을 생성한다. GlobalScope는 취소나 완료를 기다리지 않고 백그라운드에서 작동하므로 네트워크가 느릴 경우 백그라운드에서 계속 대기하여 리소스를 소모한다.

코루틴 공식 문서에서는 이러한 GlobalScope를 가능한 제거하는 것을 권장한다.

위와 같은 작업을 suspend로 만들고 coroutineScope로 그룹화한다.

suspend fun loadConfiguration() {
    val config = fetchConfigFromServer() // network request
    updateConfiguration(config)
}
// concurrently load configuration and data
suspend fun loadConfigurationAndData() {
    coroutineScope {
        launch { loadConfiguration() }
        launch { loadData() }
    }
}


2. 수명 주기 인식 CoroutineScope

ViewModelScope와 LifeCycleScope는 수명 주기 인식 컴포넌트에서 정의된 앱에서 사용할 수 있는 기본 제공 Scope이다.

2.1. ViewModelScope

viewModelScope는 앱의 각 ViewModel을 대상으로 정의된다. ViewModelScope에서 실행한 모든 코루틴은 ViewModel이 삭제되면 자동으로 취소된다. 따라서 ViewModel이 활성 상태인 경우에만 실행할 작업이 있을 때 유용하다. 작업의 범위를 ViewModel로 지정하여 ViewModel이 삭제되면 리소스를 소모하지 않도록 작업이 자동으로 취소된다.

다음은 안드로이드 디벨로퍼 공식 사이트의 ViewModelScope 예시이다.

class MyViewModel: ViewModel() {
    init {
        viewModelScope.launch {
            // Coroutine that will be canceled when the ViewModel is cleared.
        }
    }
}

2.2. LifecycleScope

LifecycleScope는 각 Lifecycle 객체에서 정의된다. LifecycleScope에서 실행한 모든 코루틴은 Lifecycle이 끝날 때 자동으로 제거된다. lifecycle.coroutineScope 또는 lifecycleOwner.lifecycleScope를 통해 Lifecycle의 CoroutineScope에 액세스할 수 있다.

아래는 안드로이드 디벨로퍼 공식 사이트의 lifecycleScope 사용 예시이다.

class MyFragment: Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewLifecycleOwner.lifecycleScope.launch {
            val params = TextViewCompat.getTextMetricsParams(textView)
            val precomputedText = withContext(Dispatchers.Default) {
                PrecomputedTextCompat.create(longTextContent, params)
            }
            TextViewCompat.setPrecomputedText(textView, precomputedText)
        }
    }
}




참고 문헌