“Android 로봇은 Google에서 제작하여 공유한 저작물을 복제하거나 수정한 것으로 Creative Commons 3.0 저작자 표시 라이선스의 약관에 따라 사용되었습니다.”
CoroutineScope 를 이용하여 프로그래밍을 할 때 주의해야할 것이 있다면
Job의 취소를 수명주기에 맞추어 작성해주어야 한다.
그렇지 않으면 앱이 종료되어도 Job은 종료되지 않고 유지되어 메모리 누수 및 배터리 드레인이 발생할 수 있다.
CoroutineScope(Dispatchers.Main).launch {
(1 .. 100).forEach {
delay(1000)
Log.d("$it")
}
}
위 소스를 실행시켜보자
위 캡쳐를 보면 onDestroy가 호출되어 앱이 종료되었음에도 Job은 종료되지 않고 프로세스를 완전히 종료시켜야 종료됨을 확인할 수 있다.
Android 프레임워크에는 이를 방지하기 위해 lifeCycleScope 라는 API를 제공한다.
lifeCycleScope를 이용하면 Activity의 수명주기에 맞춰 Job을 종료시켜준다.
아래의 샘플 소스를 살펴보자
private val sampleFlow = flow<Int> {
(1 .. 10).forEach {
delay(1000)
emit(it)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launch {
sampleFlow.collect {
Log.d("collect value $it from flow")
}
}
}
Flow를 통해서 방출되는 값들을 lifeCycleScope에서 받아서 로그로 출력하는 간단한 소스를 실행시켜보면 아래와 같다.
onDestroy가 호출되어 앱이 종료되면 Flow 스트림도 같이 종료되는 것을 확인할 수 있다.
하지만 lifeCycleScope도 단점은 존재하였는데 앱이 백그라운드로 이동하여 Pause/Stop 상태에선 중단되지 않는다.
이로인해 메모리 누수가 발생할 수 있으니 특정 LifeCycle에서만 로직을 실행하고 싶다면 다른 방법이 필요하다.
이 또한 해결책이 있으니 아래를 살펴보자
LifeCycle API 에는 repeatOnLifecycle
기능을 제공한다.
특정 LifeCycle에서만 로직을 실행하며 LifeCycle이 변하면 로직을 중단한다.
val sampleFlow = flow<Int> {
(1 .. 10).forEach {
delay(1000)
emit(it)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
sampleFlow.collect {
Log.d("collect value $it from flow")
}
}
}
}
Lifecycle.State에 대응되는 Lifecycle
- CREATED >
onCreate
- STARTED >
onStart
- RESUMED >
onResume
- INITIALIZED >
onAttach
(Fragment only)- DESTROYED >
onDestroy
예제 소스를 실행하고 로그를 확인해보면
onStop
에 진입하면 Flow 수집이 중단되고
onResume
에 다시 진입하면 Flow 수집이 다시 시작되는 것을 볼 수 있다.
에제에서 쓰였던 것처럼 포그라운드 상태에서
Flow 수집이 필요하거나 주기적으로 변하는 값의 모니터링의 필요할 경우
사용하면 좋다.
개인적으로 공부했던 것을 바탕으로 작성하다보니
잘못된 정보가 있을수도 있습니다.
인지하게 되면 추후 수정하겠습니다.
피드백은 언제나 환영합니다.
읽어주셔서 감사합니다.