본문 바로가기

Android Library

[Android Library] Flow (3) - 종류

안녕하세요.

오늘은 Jetpack Library 중 하나인 Flow의 종류에 대해 알아보겠습니다.


Cold Flow

이전 포스팅에서 봐왔던 Flow 기본 형식은 Cold Flow 입니다.

Cold Flow의 특징은 다음과 같습니다.

 

특징 - 1

  • Collector가 있을 때만 data를 emit 함

아래 코드의 출력을 보면 collect()가 있을 때

producer가 data를 stream에 넣기 시작합니다.

fun testColdFlow() = flow {
    println("cold flow is open")

    for (i in 1..4) {
        delay(100)
        emit(i)
    }

    println("cold flow is closed\n")
}

fun main() = runBlocking {
    val startTime = System.currentTimeMillis()

    testColdFlow().collect {
        println("1st -> ${it} after ${System.currentTimeMillis() - startTime}")
    }
}

 

특징 - 2

  • Collector가 수행할 때 다른 Collector가 끝날 때 까지 기다림

첫 번째 Collector가 끝나고 나서,

두 번째 Collector가 수행됩니다.

fun testColdFlow() = flow {
    println("cold flow is open")

    for (i in 1..4) {
        delay(100)
        emit(i)
    }

    println("cold flow is closed\n")
}

fun main() = runBlocking {
    val startTime = System.currentTimeMillis()

    testColdFlow().collect {
        println("1st -> ${it} after ${System.currentTimeMillis() - startTime}")
    }

    testColdFlow().collect() {
        println("2nd -> ${it} after ${System.currentTimeMillis() - startTime}")
    }
}

 

특징 - 3

  • stream에 data를 저장하지 못 함

저장을 못하기 때문에 하나의 stream이 multiple collectors를 가질 수 없습니다.

=> stream과 collector는 사실상 1:1 관계


Hot Flow

Cold Flow와 다른 Hot Flow는 아래와 같은 특징이 있습니다.

  • Collector가 없을 때도 data를 emit 함
  • stream에 data를 저장할 수 있음
    • multiple collectors를 가질 수 있음

 

Hot Flow의 대표적인 예시가 State Flow와 Shared Flow 두 가지 있습니다.

 

아래 예시를 통해 Hot Flow에 대해 더 알아보겠습니다.


State Flow

  • 단일 데이터의 상태를 표현함
  • 데이터의 변경 사항을 실시간으로 알림

 

아래 코드를 보면 StateFlow의 상태가 바뀔 때 마다 collect 돼서 출력되는 것을 볼 수 있습니다.

 

그리고 StateFlow는 가장 최신의 값 하나만을 저장합니다.

fun main(): Unit = runBlocking {
    val currentTime = System.currentTimeMillis()
    val stateFlow = MutableStateFlow(0)

    val job = launch {
        stateFlow.collect {
            println("current state : ${it} after ${System.currentTimeMillis() - currentTime}")
        }
    }

    delay(1000)
    stateFlow.value = 1

    delay(1000)
    stateFlow.value = 2

    delay(1000)
    stateFlow.value = 3

    delay(1000)
    println("hi")

    delay(1000)
    job.cancel()
}

stateFlow의 value가 바뀔 때마다 collect가 호출됩니다.


1 : N의 관계

Hot Flow는 stream과 collectors가 1 : N 관계가 가능하다고 했습니다.

 

아래 예시는 하나의 stateFlow에 3명의 구독자가 있습니다.

stateFlow의 상태가 바뀔 때 마다 3명의 구독자가 알아차리고 연산을 시작합니다.

fun main(): Unit = runBlocking {
    val currentTime = System.currentTimeMillis()
    val stateFlow = MutableStateFlow(0)

    val job1 = launch {
        stateFlow.collect {
            println("job1 : ${it} after ${System.currentTimeMillis() - currentTime}")
        }
    }

    val job2 = launch {
        stateFlow.collect {
            println("job2 : ${it} after ${System.currentTimeMillis() - currentTime}")
        }
    }

    val job3 = launch {
        stateFlow.collect {
            println("job3 : ${it} after ${System.currentTimeMillis() - currentTime}")
        }
    }

    delay(1000)
    stateFlow.value = 1

    delay(1000)
    stateFlow.value = 2

    delay(1000)
    stateFlow.value = 3

    delay(1000)
    println("hi")

    delay(1000)
    job1.cancel()
    job2.cancel()
    job3.cancel()
}

값이 바뀔 때 마다 3개의 collect가 실행되는 것을 볼 수 있습니다.


LiveData와 차이점?

StateFlow와 LiveData는 비슷한 점이 있습니다.

  • 둘 다 관찰 가능한 데이터 홀더 클래스

 

하지만 이런 차이점이 존재합니다.

  StateFlow LiveData
초기 상태 초기 상태를 생성자에게 전달해야 함 그렇지 않음
Life Cycle - view의 life cycle에 맞춰 실행하지 않음
- 메모리 누수가 발생할 수 있기에
Lifecycle.repeatOnLifecycle 블록에서 써야 취소가 됨
- view의 life cycle에 맞춰 실행함
- 뷰가 STOPPED 상태가 되면 자동으로 등록 취소함 

Shared Flow

  • StateFlow의 일반화 버전
    • Flow -> (상속) -> ShareFlow -> (상속) -> StateFlow
  • 단일 값이 아닌, 원하는 개수의 데이터를 저장함
  • 다중 구독자 간에 데이터를 공유하고 처리할 때 사용됨
  • Buffer 처럼 사용할 수 있음

 

안드로이드 공식 문서에서 가져온 코드입니다.

(https://developer.android.com/kotlin/flow/stateflow-and-sharedflow?hl=ko)

앱 전체에 tick을 보내서, 정기적으로 앱의 view 들이 refresh될 수 있도록 하는 예제 코드입니다.

class TickHandler(
    private val externalScope: CoroutineScope,
    private val tickIntervalMs: Long = 5000
) {
    // Backing property to avoid flow emissions from other classes
    private val _tickFlow = MutableSharedFlow<Unit>(replay = 0)
    val tickFlow: SharedFlow<Event<String>> = _tickFlow

    init {
        externalScope.launch {
            while(true) {
                _tickFlow.emit(Unit)
                delay(tickIntervalMs)
            }
        }
    }
}

class NewsRepository(
    ...,
    private val tickHandler: TickHandler,
    private val externalScope: CoroutineScope
) {
    init {
        externalScope.launch {
            // Listen for tick updates
            tickHandler.tickFlow.collect {
                refreshLatestNews()
            }
        }
    }

    suspend fun refreshLatestNews() { ... }
    ...
}

여기까지 Flow의 종류에 대해 알아봤습니다.

감사합니다.


참고 자료

https://medium.com/@amitshekhar/cold-flow-vs-hot-flow-ec25da7c198a

 

Cold Flow vs Hot Flow

In this blog, we will learn about Cold Flow vs Hot Flow in Kotlin.

medium.com

https://developer.android.com/kotlin/flow/stateflow-and-sharedflow?hl=ko 

 

StateFlow 및 SharedFlow  |  Kotlin  |  Android Developers

StateFlow 및 SharedFlow 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. StateFlow와 SharedFlow는 흐름에서 최적으로 상태 업데이트를 내보내고 여러 소비자에게 값을

developer.android.com