lifecycleScope의 한계점
flow를 Activity의 lifecycleScope에서 사용하게 되면 Activity가 onDestroy 될 때 데이터 collect가 중단된다. 하지만 onDestroy는 Activity가 종료될 때 수행되고 Activity가 백그라운드로 내려갈 때 수행되지 않는다. 앱이 백그라운드로 내려가면 activity의 lifecycle은 onStop시 수행되므로 lifecycleScope의 collect는 계속해서 데이터를 수집하는 것이다.
참조 : [Android CoroutineScope] 1. Activity, ViewModel에서 올바른 CoroutineScope 사용법 : lifecycleScope과 viewModelScope의 활용
기존의 해결책
기존에는 이 문제를 해결하기 위해 onStart에서 collect를 시작하고 onStop에서 collect를 중지하는 작업을 했다.
하지만 이는 보일러 플레이트 코드를 만들어 냈으며, 이로 인해 사람이 실수할 경우 불필요하게 데이터 수집이 백그라운드에서 일어날 수 있었다.
참조 : [Android CoroutineScope] 2. Activity lifecycleScope에서의 Coroutine Job 사용 시의 한계점과 해결 방안
lifecycleScope 내부에서 repeatOnLifecycle 사용해 collect 동작 최적화하기
코루틴의 새로운 버전에서는 위의 문제를 처리하기 위한 repeatOnLifecycle이라는 메서드가 생겼다. repeatOnLifecycle은 Activity가 포그라운드에 있을 때로 한정지어, 특정 Lifecycle이 Trigger 되었을 때 동작하도록 만드는 block이다.
public suspend fun LifecycleOwner.repeatOnLifecycle(
state: Lifecycle.State,
block: suspend CoroutineScope.() -> Unit
): Unit = lifecycle.repeatOnLifecycle(state, block)
repeatOnLifecycle 메서드의 주석에는 다음과 같이 쓰여있다.
// Runs the block of code in a coroutine when the lifecycle is at least STARTED.
// The coroutine will be cancelled when the ON_STOP event happens and will
해석: LifecycleOwner(Activity, Fragment)의 Lifecycle이 최소 onStart가 되었을 때 블록에 있는 메서드들이 수행되며, onStop되었을 때는 코루틴 Job이 취소된다.
풀어서 해석: onStart ~ onStop Lifecycle 범위 내에서 특정 Lifecycle.State(onStart, onResume, onPause, onStop)이 Trigger될 때마다 블록에 대한 Coroutine Job을 생성하고, onStop에서 Job을 취소하도록 만드는 것이다.
간단히 말하면, repeatOnLifecycle은 Coroutine Job이 생성될 Lifecycle.State을 인자로 받아 특정 생명주기가 포그라운드에 있을 때까지만(onStart ~ onStop) 동작하는 Coroutine Job을 생성해주는 메서드이다. 즉, repeatOnLifecycle 블록 내부에 있는 flow에 대한 collect는 액티비티가 포그라운드에 있을 때만 진행된다.
class MainActivity : ComponentActivity() {
private val stringFlow: Flow<String> = flow {
for (i in 0..1000) {
emit("integer: $i")
delay(1000)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
stringFlow.collect {
println(it)
}
}
}
}
}
따라서 위의 코드를 수행하면 다음과 같이 홈버튼을 누를때(onStop 시킬 때), 백버튼을 누를 때(onDestroy 시킬 때) 모두 Coroutine Job이 cancel되는 것을 확인할 수 있다.
repeatOnLifecycle의 의의
repeatOnLifecycle을 활용하면 포그라운드에서 안전하게 Coroutine Job을 수행할 수 있게 된다. 이번 글의 요점은 포그라운드에서만 작업되어야 하는 코루틴 블록은 repeatOnLifecycle을 활용해 작업해야 한다는 것이다.
백그라운드에서 작업이 일어나야 하는 일에서는 repeatOnLifecycle을 쓰면 안된다!