안드로이드 시스템·Compose 용어 사전
코루틴collectAsStateWithLifecycle · flowWithLifecycle

repeatOnLifecycle

화면이 지정 상태 이상일 때만 Flow를 수집하고, 내려가면 취소하는 API.

화면이 지정 상태 이상일 때만 블록을 실행하고, 아래로 내려가면 취소한다.

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 로 감싼다

면접 함정

  • repeatOnLifecyclelaunch 없이 부른다 → 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 — 버퍼에 남았다가 재구독 시 전달된다

  • "백그라운드에서 온 이벤트를 놓치면 안 되는가" 로 자료구조를 고른다

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 코루틴 on Android — lifecycleScope·repeatOnLifecycle