안드로이드 탐구 : collect & collectLatest

Skele·2025년 4월 1일

collect & collectLatest

안드로이드에서 다양한 Flow를 다루다보면 자주 마주치게 되는 함수들이다.
무슨 차이가 있는 것일까?

collect

collect모든 값을 순차적으로 emit하며, 각 값이 완전히 처리될 때까지 다음 값을 대기한다.
예시를 보도록 하자.

fun main() {
   val countDownFlow = flow{
       val initialValue = 10
       var currentValue = initialValue

       println("emitting value : $currentValue at ${formatter.format(System.currentTimeMillis())}")
       emit(initialValue)

       while(currentValue > 0) {
           delay(1000L)
           currentValue--
           println("emitting value : $currentValue at ${formatter.format(System.currentTimeMillis())}")
           emit(currentValue)
       }
   }
    collectFlow(countDownFlow)
}

1초마다 숫자를 내림차순으로 발행하는 Flow가 있다.

fun collectFlow(countDownFlow: Flow<Int>) {
    runBlocking {
        countDownFlow.collect{ time ->
            delay(1500L)
            println("collected value : $time at ${formatter.format(System.currentTimeMillis())}")
        }
    }
}

collect는 1.5초마다 이루어진다고 한다면 결과는 어떻게 될까?

emitting value : 10 at 18:00:39.333
collected value : 10 at 18:00:40.977
emitting value : 9 at 18:00:41.999
collected value : 9 at 18:00:43.512
emitting value : 8 at 18:00:44.517
collected value : 8 at 18:00:46.026
emitting value : 7 at 18:00:47.040
collected value : 7 at 18:00:48.541
emitting value : 6 at 18:00:49.557
collected value : 6 at 18:00:51.062
emitting value : 5 at 18:00:52.070
collected value : 5 at 18:00:53.576
emitting value : 4 at 18:00:54.584
collected value : 4 at 18:00:56.100
emitting value : 3 at 18:00:57.103
collected value : 3 at 18:00:58.615
emitting value : 2 at 18:00:59.619
collected value : 2 at 18:01:01.128
emitting value : 1 at 18:01:02.136
collected value : 1 at 18:01:03.643
emitting value : 0 at 18:01:04.646
collected value : 0 at 18:01:06.159

보다 싶이 emit으로부터 collection까지, collect가 이루어지는 블록의 소요시간인 1.5초가 걸리는 것을 볼 수 있다.
또한, collect의 딜레이가 Flow 블록의 딜레이보다 길더라도 모든 값이 순차적으로 emit되며, collection 왈료로부터 emit까지 Flow의 딜레이인 1초가 걸린다는 것을 알 수 있다.
즉, collect를 사용하면 Flow가 값을 emit했을 때 collect가 완료되기 전까지 suspend되고 완료되면 다시 루프를 수행한다.

collectLatest

collectLatest가장 최신 값만 수집하며, 새 값이 오면 이전 처리를 취소하고 최신 값으로 덮어쓴다.
동일한 Flow에 대해서 collectLatest를 예시로 들어보자.

fun collectLatestFlow(countDownFlow: Flow<Int>) {
    runBlocking {
        countDownFlow.collectLatest{ time ->
            delay(1500L)
            println("collected latest value : $time at ${formatter.format(System.currentTimeMillis())}")
        }
    }
}

단순히 collectcollectLatest로 바꾼 코드이다.

emitting value : 10 at 18:01:06.371
emitting value : 9 at 18:01:07.399
emitting value : 8 at 18:01:08.428
emitting value : 7 at 18:01:09.430
emitting value : 6 at 18:01:10.433
emitting value : 5 at 18:01:11.448
emitting value : 4 at 18:01:12.453
emitting value : 3 at 18:01:13.464
emitting value : 2 at 18:01:14.472
emitting value : 1 at 18:01:15.478
emitting value : 0 at 18:01:16.489
collected latest value : 0 at 18:01:17.994

collect와는 달리, 마지막으로 emit된 값이 처리된 것을 볼 수 있다.
이것은 emit에 걸리는 딜레이 1초보다 collect에 걸리는 딜레이가 1.5초로 더 길기 때문에 10emit되어 collect가 수행되고 있는 도중에 다시 9emit되면 기존의 10에 대한 작업이 취소된다.
따라서 더이상 emit할 값이 없어서 작업이 취소되지 않는 0만이 제대로 collect된 것을 볼 수 있다.
즉, collectLatestcollect와 달리 emit에서 Flow가 suspend되지 않는다.(정확히는 값을 보내고 바로 넘어가는 것)

Usage

collect

순차적으로 모든 값이 collect 되는 것을 보장하기 때문에, 모든 값이 처리가 되어야하는 데이터 저장, 통신 등에서 사용된다.

collectLatest

이전 값에 상관없이 빠르게 가장 최신의 값만을 처리하기 때문에, 값이 자주 바뀌면서 이전 값이 필요 없는 UI 상태를 반영할 때 자주 사용된다.

profile
Tireless And Restless Debugging In Source : TARDIS

0개의 댓글