본문 바로가기

카테고리 없음

Understand Android Lifecycle Aware Coroutine Scope Made Easy

728x90
반응형

출처 : https://medium.com/mobile-app-development-publication/understand-android-lifecycle-aware-coroutine-scope-made-easy-f86d13d16f0e

Kotlin Coroutine을 사용하려면 적절한 scope 를 정의해야 합니다. 까다로운 부분은 생성하고 중지하는 것을 기억해야 한다는 것입니다. 이를 고려하여 Google Android 개발 팀은 수명 주기를 인식하는 몇 가지 Coroutine scope 를 제공했습니다.

 

예제에서 Activity에 lifecycleScope를 사용하면 Fragment에 viewLifecycleOwner.lifecycleScope가 사용됩니다.

 

Fragment 체크아웃에 대해 구체적으로 배우려면 다음 포스트를 확인하세요.

Understanding of Coroutine Scope Behavior for Fragment

 

1. 특정 수명 주기에서 매번 Coroutine scope 를 반복(다시 시작)합니다.

 

때로는 특정 수명 주기에 있을 때마다 일부 기능을 실행하고 수명 주기 단계를 벗어나면 즉시 중지해야 하는 경우가 있습니다(예: LiveData observer).

 

 

이해를 돕기 위해 카운터 코드를 작성했습니다.

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

    lifecycleScope.launch {
        repeatOnLifecycle(Lifecycle.State.STARTED) {
            var value = 0
            repeat(10) {
                delay(1000)
                Log.d("Track", "value ${++value}")
            }
        }
    }
}
 

 

다양한 시나리오에서 실행하면 카운터가 다시 시작되는 것을 볼 수 있습니다. 이는 수명 주기가 onStart에 도달할 때 코루틴이 항상 다시 시작되기 때문입니다. onStop 때 이전 coroutine scope 가 종료되었습니다.

 
// Starting
D/Track: value 1
D/Track: value 2

// onBackground and onForeground
D/Track: value 1
D/Track: value 2

// onProcess Death and Restart
D/Track: value 1
D/Track: value 2

// onRotate Screen
D/Track: value 1
D/Track: value 2

 

2. 특정 수명 주기에서 매번 coroutine scope 를 일시 중단 하고 계속하기

 

때로는 특정 수명 주기에 있을 때마다 일부 기능을 실행하고 수명 주기를 벗어날 때 일시 중단 해야 합니다. 수명 주기에 다시 들어갈 때 프로세스를 계속해야 합니다. (예: 화면 로딩 이벤트)

 

이해를 돕기 위해 카운터 코드를 작성했습니다.

lifecycleScope.launchWhenStarted {
    var value = 0

    repeat(10) {
       delay(1000)
       Log.d("Track", "value ${++value}")
    }
}

 

다양한 시나리오에서 실행하면 onBackground 및 onForground일 때 카운터가 일시 중단되고 다시 시작되는 것을 볼 수 있습니다. 그러나 다른 시나리오(프로세스 종료 및 회전)에서는 onStop 때 이전 coroutine scope 가 종료되어 다시 시작됩니다.

// Starting
D/Track: value 1
D/Track: value 2

// onBackground and onForeground
D/Track: value 3
D/Track: value 4

// onProcess Death and Restart
D/Track: value 1
D/Track: value 2

// onRotate Screen
D/Track: value 1
D/Track: value 2

 

한 가지 참고 사항은 코루틴이 onBackground 전에 완료되면 onForeground일 때 계속되거나 다시 시작되지 않는다는 것입니다. 그러나 복원 또는 구성이 변경되면 다시 시작됩니다.

 

사용할 수 있는 완전히 동일한 API가 또 있지만 아래와 같이 한 줄만 더 깁니다.

lifecycleScope.launch {
    whenStarted {
        var value = 0

        repeat(10) {
            delay(1000)
            Log.d("Track", "value ${++value}")
        }
    }
}

 

이것의 이점은 더 많은 제어가 가능하다는 것입니다. 각 scope 는 모두 차단되어 있으므로 각각은 하나씩만 계속됩니다.

lifecycleScope.launch {
    // Do something before started
    whenStarted {
        // Do something if onStarted or later
    }
    whenResumed {
        // Do something if onResume or later
    }
    // Do something after resumed
}

 

3. Configuration이 변경되더라도 Coroutine Scope 를 계속 유지합니다(예: 회전).

 

때로는 onBackground에 있거나 화면을 회전하는 경우에도 계속 실행되는 코루틴이 필요합니다(예: 네트워킹 가져오기 프로세스).

 

 

이는 ViewModel에서만 수행할 수 있습니다. ViewModel에는 viewModelScope 가 있습니다. 이해를 돕기 위해 카운터 코드를 작성했습니다.

 
class LifeCycleScopeViewModel : ViewModel() {

    init {
        viewModelScope.launch {
            var value = 0

            repeat(10) {
                delay(1000)
                Log.d("Track", "value ${++value}")
            }
        }
    }
}

다양한 시나리오에서 실행하면 하나가 onBackground이거나 회전하는 경우에도 카운터가 계속 계산되는 것을 볼 수 있습니다. 그러나 다른 시나리오(프로세스 종료 및 회전)에서는 이전 coroutine scope 가 종료되면서 다시 시작됩니다.

 
// Starting
D/Track: value 1
D/Track: value 2

// onBackground and onForeground
D/Track: value 3
D/Track: value 4

// onProcess Death and Restart
D/Track: value 1
D/Track: value 2

// onRotate Screen
D/Track: value 3
D/Track: value 4

 

 

참고 : 프로세스가 종료 되면 모든 하위 프로세스도 종료되므로 CoroutineScope(...)를 사용하는 것보다 viewModelScope를 사용하는 것이 좋습니다.

 

Summary of behavior

 

동작을 요약하면 다음과 같습니다.

 

 

참고: Process Death 동안 모든 코루틴이 종료되고 복원 시 다시 시작됩니다. 따라서 다시 시작하지 않으려면 savedStateInstance 에서 이에 대한 일부 logic 를 처리하기 위한 일부 정보를 유지해야 합니다.

 

개인적인 생각 :

launchWhenStarted 와 repeatOnLifecycle API 에 대해서 자세히 본적이 없었는데 해당 API 를 썼을 경우에 차이점에 대해서 비교하는 내용이 좋았습니다.

lifecycleScope.launch {
    // Do something before started
    whenStarted {
        // Do something if onStarted or later
    }
    whenResumed {
        // Do something if onResume or later
    }
    // Do something after resumed
}

이런 형태를 처음 보았고, 필요시에 이렇게 사용 하는 것도 좋을 것 같다는 생각이 들었습니다.

 
 

 

 

728x90
반응형