Image
Coroutines/CoroutineScope

[Android CoroutineScope] 1. Activity, ViewModel에서 올바른 CoroutineScope 사용법 : lifecycleScope과 viewModelScope의 활용

Android에서 올바른 CoroutineScope를 사용해야 하는 이유

CoroutineScope는 Coroutine Job이 실행되는 Scope이다. CoroutineScope가 해제되면 CoroutineScope에 속한 Coroutine Job들은 모두 해제된다. 따라서 만약 CoroutineScope이 안드로이드 구성요소(Activity, ViewModel)의 Lifecycle에 따라 올바로 할당 해제되지 않는다면 해제되어야 하는 Job들이 계속해서 동작해서 Memory Leak으로 이어진다

 

예를들어 App의 Lifecycle동안 유지되는 GlobalScope를 1초마다 string을 내보내는 flow를 collect하기 위해 사용해보자.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            //UI 코드 작성
        }

        val stringFlow : Flow<String> = flow{
            for(i in 0..1000) {
                emit("integer: $i")
                delay(1000)
            }
        }

        GlobalScope.launch {
            stringFlow.collect {
                println(it)
            }
        }
    }
}

위 코드를 작성한 다음 App을 실행하면 Activity에서 뒤로가기를 눌러 Activity가 onDestroy되더라도 여전히 stringFlow에 대한 collect가 수행된다.

 

아래는 실제 수행화면이다. integer : 5가 호출됐을 때 뒤로가기를 눌렀는데도 여전히 collect가 일어나는 것을 볼 수 있고 다시 activity를 수행했을 때 이전 Coroutine job이 cancel이 안된 상태에서 다시 새로운 Coroutine job이 수행되어 collect가 일어나는 것을 볼 수 있다.

그림1. Memory Leak이 일어나는 모습

즉, 해당 코루틴 Job에 대한 제어권을 잃은 상태로 코루틴 Job이 계속 실행되어, Memory Leak이 일어난다.

 

이에 대해서는 Android Studio에서는 만약 GlobalScope를 사용할거면 섬세한 관리가 필요하다고 Warning 또한 표시해준다. 잘못해서라도 사용하는 일이 없도록 하자.

그림2. GlobalScope Warning

 

 

안드로이드를 위한 CoroutineScope : lifecycleScope, viewModelScope

우리는 Coroutine 사용시 메모리 릭을 방지하기 위해 Activity에서는 Activity의 Lifecycle에 맞춰진 CoroutineScope을 사용해야 하며, ViewModel에서도 ViewModel의 Lifecycle에 맞춰진 CoroutineScope를 사용해야 한다.

 

Activity에서 사용해야 하는 lifecycleScope

Activity에서는 LifecycleOwner 인터페이스의 확장 프로퍼티로 선언된 lifecycleScope를 사용하면 된다.

public val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
    get() = lifecycle.coroutineScope

 

이 Scope를 사용하면 Activity가 onDestroy될 때 lifecycleScope또한 해제되어 내부 Coroutine Job 들이 취소된다.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            //UI 코드 작성
        }

        val stringFlow : Flow<String> = flow{
            for(i in 0..1000) {
                emit("integer: $i")
                delay(1000)
            }
        }

        lifecycleScope.launch {
            stringFlow.collect {
                println(it)
            }
        }
    }
}

아래는 위의 코드를 실행했을 때 결과이다. Activity에서 뒤로 가기를 누르면 onDestroy가 호출되므로 lifecycleScope가 취소되어 stringFlow를 collect 하는 Job또한 중지됨을 확인할 수 있다.

그림3. Activity에서 lifecycleScope사용하기

 

ViewModel에서 사용해야 하는 viewModelScope

ViewModel에서 또한 Acitivty에서와 마찬가지로 ViewModel의 확장 프로퍼티로 선언된 viewModelScope를 쓰면된다.

public val ViewModel.viewModelScope: CoroutineScope

 

ViewModel은 Fragment혹은 Activity의 Lifecycle에 binding되므로 viewModelScope는 binding된 lifecycle에 맞춰 viewModelScope내의 Job에 대한 취소를 하도록 한다. 만약 ViewModel이 어떻게 생성되는지 모른다면 아래 글을 보고 오도록 하자.

[ViewModel] 2. ViewModel은 어떻게 저장되는가?

 

예시 코드는 다음과 같다. ViewModel 클래스 내부에서는 Coroutine Job을 생성할 때 viewModelScope를 사용한다.

class MainViewModel() : ViewModel() {
    val stringFlow : Flow<String> = flow{
        for(i in 0..1000) {
            emit("integer: $i")
            delay(1000)
        }
    }

    fun collectStringFlow(){
        viewModelScope.launch {
            stringFlow.collect {
                println(it)
            }
        }
    }
}

 

lifecycleScope 사용의 한계점

하지만 lifecycleScope에도 여전히 한계점이 있다. 바로 onDestroy 시 collect가 중단된다는 것이다. 이 말은 앱이 백그라운드로 내려가고 종료되지 않았을 때 데이터가 계속 수집된다는 것을 뜻한다.

 

이것이 왜 문제가 되는지 해결 방안은 무엇인지 다음 글에서 알아보도록 하자.

[Android CoroutineScope] 2. Activity lifecycleScope에서의 Coroutine Job 사용 시의 한계점과 해결 방안

 

[Android CoroutineScope] 2. Activity lifecycleScope에서의 Coroutine Job 사용 시의 한계점과 해결 방안

lifecycleScope 사용의 한계점 lifecycleScope만을 사용해 Coroutine Job을 사용하는 것은 한계점이 있다. 바로 onDestroy 시 Job이 cancel된다는 것이다. onDestroy 시 job이 cancel 된다는 것은 백그라운드로 내..

kotlinworld.com

 

반응형

 

이 글의 저작권은 '조세영의 Kotlin World' 에 있습니다. 글, 이미지 무단 재배포 및 변경을 금지합니다.

 

 

Kotlin, Android, Spring 사용자 오픈 카톡

오셔서 궁금한 점을 질문해보세요!
비밀번호 : kotlin22

open.kakao.com