화면이 지정 상태 이상일 때만 블록을 실행하고, 아래로 내려가면 취소한다.
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { render(it) }
}
}
- STARTED 로 올라가면 — 블록을 새 코루틴으로 실행한다
- STARTED 아래로 가면 — 그 코루틴을 취소한다
- 다시 올라오면 — 블록을 다시 실행한다 (반복된다 — 그래서 repeat 다)
왜 필요한가
// ❌ 화면이 백그라운드로 가도 계속 수집한다
lifecycleScope.launch { viewModel.uiState.collect { render(it) } }
보이지도 않는 화면을 위해
- 네트워크·DB 를 계속 읽는다 (데이터·배터리 낭비)
- 뷰를 갱신하려다 파괴된 뷰를 건드린다
반대로 launchWhenStarted 는 취소가 아니라 '일시정지' 라
- 업스트림(네트워크 구독) 은 계속 살아 있었다 — 그래서 폐기됐다
Compose에서는
val state by viewModel.uiState.collectAsStateWithLifecycle()
내부적으로 repeatOnLifecycle 을 쓴다 collectAsState() 는 생명주기를 모른다 — 백그라운드에서도 수집한다
- 안드로이드에서는 거의 항상 WithLifecycle 쪽이 맞다
어느 상태를 고르나
- STARTED — 화면이 보이는 동안. 대부분 이것
- RESUMED — 포커스가 있을 때만. 다이얼로그가 뜨면 멈춘다
- CREATED — 거의 쓰지 않는다
Fragment에서 반드시 viewLifecycleOwner
// ❌ Fragment 생명주기 — 백스택에서 뷰가 죽어도 계속 돈다
lifecycleScope.launch { repeatOnLifecycle(...) { ... } }
// ✅ 뷰 생명주기
viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(...) { ... } }
여러 Flow를 수집할 때
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch { viewModel.uiState.collect { render(it) } }
launch { viewModel.events.collect { handle(it) } }
}
}
collect 는 suspend 라 첫 번째가 끝나지 않으면 두 번째에 도달하지 않는다
- 각각 launch 로 감싼다
면접 함정
- ❌
repeatOnLifecycle을launch없이 부른다 → suspend 함수라 코루틴 안에서만 부를 수 있다. - ❌ "블록이 한 번만 실행된다" → 상태가 오르내릴 때마다 반복 실행된다. 여기서 일회성 초기화를 하면 중복된다.
launchWhenStarted는 왜 폐기됐나
lifecycleScope.launchWhenStarted { flow.collect { } }
- 동작 — STARTED 아래로 가면 코루틴을 '일시정지' 한다
- 문제 — 업스트림은 계속 살아 있다
- 네트워크 구독이 유지된다
- Flow 가 계속 값을 만들고 버퍼에 쌓인다
- 돌아오면 밀린 값이 한꺼번에 밀려온다
repeatOnLifecycle 은 '취소' 한다 — 업스트림까지 정리된다
flowWithLifecycle
// Flow 하나만 다룰 때 더 간결하다
viewLifecycleOwner.lifecycleScope.launch {
viewModel.uiState
.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED)
.collect { render(it) }
}
내부적으로 repeatOnLifecycle 을 쓴다 Flow 가 여러 개면 repeatOnLifecycle + 개별 launch 가 낫다
- (flowWithLifecycle 를 여러 개 쓰면 생명주기 구독도 여러 개가 된다)
값이 유실되는 것을 이해한다
-
StateFlow — 최신 값을 replay 하므로 재구독 시 현재 상태를 받는다 → 문제없다
-
SharedFlow(replay=0) — 중단 중에 발생한 이벤트는 사라진다
-
Channel — 버퍼에 남았다가 재구독 시 전달된다
-
"백그라운드에서 온 이벤트를 놓치면 안 되는가" 로 자료구조를 고른다