[코루틴 (12)] LifecycleScope & 안드로이드 생명주기 대응
지난 11편에서는 WorkManager + 코루틴 연동을 통해 백그라운드 작업을 안정적으로 처리하는 방법을 살펴보았습니다. 이번 글에서는 안드로이드 생명주기(Lifecycle)와 코루틴을 자연스럽게 결합하는 lifecycleScope, viewLifecycleOwner.lifecycleScope 등 다양한 스코프를 살펴보며, 메모리 누수를 예방하고 취소 시점을 자동으로 관리하는 방법을 알아보겠습니다.
핵심 키워드
• LifecycleScope
• viewLifecycleOwner.lifecycleScope
• launchWhenStarted / launchWhenCreated / launchWhenResumed
• 메모리 누수(Leak) 방지
• 생명주기 안전한 코루틴
1. 안드로이드 생명주기와 코루틴
1.1 기존 문제: 메모리 누수 & 중단되지 않는 비동기 작업
안드로이드에서 오래전부터 Activity나 Fragment가 사라져도, 이전에 실행했던 스레드나 비동기 작업이 계속 동작해 메모리 누수가 발생하거나, UI 업데이트가 불가능한 상황이 종종 있었습니다.
• 예: HTTP 요청이 끝날 때까지 백그라운드 스레드가 살아 있음 → Activity 종료 후에도 콜백이 불필요하게 호출됨 → 충돌 또는 메모리 누수.
1.2 LifecycleScope 해결책
코루틴과 안드로이드 Lifecycle을 접목하면, Activity/Fragment가 DESTROY 상태가 될 때 자동으로 코루틴이 취소(Cancellation)되어 자원 누수를 막아줄 수 있습니다.
• lifecycleScope는 Lifecycle 객체에 연동된 스코프이며, Lifecycle이 onDestroy에 도달하면 취소됩니다.
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
// 생명주기가 onDestroy()되면 이 코루틴 취소
fetchDataAndDisplay()
}
}
}
• 이처럼 launch 블록 안에서 suspend 함수를 호출하면, Activity가 파괴될 때 자동으로 작업이 중단됩니다.
2. Fragment에서의 viewLifecycleOwner.lifecycleScope
2.1 ViewLifecycle vs FragmentLifecycle
Fragment에는 두 가지 Lifecycle이 있습니다.
1. Fragment 자체 Lifecycle: this.lifecycle — Fragment가 생성되고, onDestroy()될 때까지
2. View Lifecycle: viewLifecycleOwner.lifecycle — onCreateView~onDestroyView 사이만 유효
• ViewLifecycle는 View가 재생성되는 시점(예: Fragment onDestroyView, onCreateView)와 맞물려, UI 관련 코루틴을 자동 취소/재시작하기 좋습니다.
2.2 예시: viewLifecycleOwner.lifecycleScope
class MyFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
// View가 사라지면 (onDestroyView)
// 이 코루틴도 취소
updateUiContinuously()
}
}
}
• UI 로직(예: RecyclerView 업데이트, LiveData/Flow 수집 등)을 viewLifecycleOwner.lifecycleScope에서 수행하면, View가 파괴될 때 자동으로 취소되어 Fragment 재생성 시 충돌이나 메모리 누수를 예방합니다.
• 반면, Fragment 자체 Lifecycle(lifecycleScope)을 이용하면 onDestroy() 시점까지 살아있을 수 있으므로, UI보다 오랜 생존이 필요한 작업일 때 적합합니다.
3. launchWhenStarted, launchWhenCreated, launchWhenResumed
3.1 용도 개요
안드로이드 KTX 확장 함수로 제공되는 launchWhenXxx 계열은 특정 Lifecycle 상태에 도달했을 때 코루틴을 실행하고, 해당 상태를 벗어나면 일시 중단하는 방식을 지원합니다.
• launchWhenCreated: Lifecycle이 CREATED 상태가 되면 코루틴 실행, 이후 상태가 DESTROYED되면 취소
• launchWhenStarted: Lifecycle이 STARTED 상태가 되면 코루틴 실행, STOPPED 이하로 떨어지면 코루틴 일시 중단
• launchWhenResumed: Lifecycle이 RESUMED 상태일 때만 코루틴 실행, PAUSED 이하로 떨어지면 일시 중단
lifecycleScope.launchWhenStarted {
// Activity/Fragment가 STARTED~RESUMED 범위일 때만 코드 실행
// STOPPED 되면 일시 중단
observeUiState()
}
3.2 이점과 주의사항
• 이점: Lifecycle 상태가 내려가면 자동으로 코루틴 일시 중단, 올라가면 재개 → UI 업데이트를 안전하게 수행 가능.
• 주의: 연속적으로 화면 전환이 잦은 경우, “일시 중단 → 재개”가 자주 일어나면서 연결/취소 로직이 빈번히 실행될 수 있으므로, 성능이나 데이터 일관성에 신경 써야 합니다.
4. Flow와 생명주기 대응
4.1 repeatOnLifecycle 블록
코틀린 Flow를 Lifecycle에 맞춰 안전하게 collect하려면 repeatOnLifecycle 함수가 권장됩니다.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
uiStateFlow.collect { state ->
// STARTED 이상인 상태에서만 수집
updateUi(state)
}
}
}
• Lifecycle이 STARTED 이상이면 Flow를 collect, STOPPED 이하로 내려가면 collect를 중단. 다시 STARTED 이상으로 올라가면 collect 재개.
• 이는 launchWhenXxx 계열보다 더욱 권장되는 최신 API로, Android 공식 문서에서도 강조됩니다.
4.2 안드로이드 Compose와의 결합
• Compose에서 StateFlow나 SharedFlow를 collectAsState() 등으로 구독할 경우, LifecycleScope 대신 Compose 자체 리컴포지션 사이클에 맞춰 수집이 이루어지기도 합니다.
• Fragment나 Activity에서 Flow를 사용해 UI 업데이트하는 전통적인 방식이라면 repeatOnLifecycle + Flow collect가 안정적인 패턴입니다.
5. 실무 적용 시나리오
1. Activity에서 데이터 로딩
• onCreate()에서 lifecycleScope.launch로 네트워크/API 호출. Activity가 사라지면 작업 자동 취소.
2. Fragment + View Lifecycle
• viewLifecycleOwner.lifecycleScope로 UI 로직을 관리해, onDestroyView() 시점에 코루틴 취소.
3. launchWhenStarted
• 화면이 실제로 보여지는 시점(STARTED~RESUMED)에만 UI 업데이트 수집. 예: 영상 스트리밍, BLE 디바이스 스캔 등.
4. repeatOnLifecycle
• Flow로 UI 상태를 구독할 때, Lifecycle이 특정 상태 이상일 때만 collect를 실행해 리소스 절약.
6. 마무리 및 다음 예고
이번 [12편]에서는 LifecycleScope & 안드로이드 생명주기 대응을 중점적으로 다루었습니다.
• lifecycleScope: Activity/Fragment Lifecycle에 맞춰 코루틴 자동 취소
• viewLifecycleOwner.lifecycleScope: Fragment UI 전용 생명주기에서 코루틴을 관리, onDestroyView() 시점에 취소
• launchWhenXxx, repeatOnLifecycle: Lifecycle 상태가 변경될 때 코루틴을 일시 중단/재개하여 UI 업데이트를 안전하게 처리
다음 13편에서는 Compose + 코루틴 + Flow 시너지를 주제로, Jetpack Compose UI와 코루틴을 결합해 선언형 UI에서 비동기 로직을 어떻게 다룰 수 있는지 보다 심층적으로 살펴보겠습니다.
시리즈 전체 목차
1. 코루틴 기초(suspend, CoroutineScope, Dispatcher)
2. Structured Concurrency와 코루틴 스코프 관리
5. Flow 심화(Flow, StateFlow, SharedFlow)
6. 안드로이드 실무 적용(Retrofit, Room, ViewModel)
7. 동시성 문제 해결(Mutex, Semaphore)