공유플로우와 상태플로우

개요

  • 일반적으로 플로우는 콜드 데이터이기 때문에 요청할 때마다 값이 계산됩니다.
  • 여러 개의 수신자가 하나의 데이터가 변경되는지 감지하는 경우도 있습니다.
  • 이 경우 메일링 리스트와 비슷한 개념인 SharedFlow를 활용할 수 있습니다.
  • StateFlow는 감지 가능한 값과 비슷하게 동작합니다.

SharedFlow

  • 공유플로우를 통해 메시지를 보내면, 대기하고 있는 모든 코루틴이 수신하게 됩니다.
  • 이는 브로드캐스트 채널과 비슷하게 동작합니다.

replay

  • 마지막으로 전송한 값들을 저장할 수를 지정합니다.(default:0)

relayCache

  • 값을 저장한 캐시를 나타냅니다.

resetReplayCache

  • 저장한 캐시를 초기화하는 경우 활용합니다.
suspend fun main(): Unit = coroutineScope {
    // replay : 마지막으로 전송한 값들을 정한 수만큼 저장, default : 0
    // 코루틴이 감지를 시작하면 저장된 값들을 먼저 받음
    val mutableSharedFlow = MutableSharedFlow<String>(
        replay = 2
    )
    mutableSharedFlow.emit("message 1")
    mutableSharedFlow.emit("message 2")
    mutableSharedFlow.emit("message 3")

    //replayCache : 값을 저장한 캐시를 나타냄
    println(mutableSharedFlow.replayCache)

    launch {
        mutableSharedFlow.collect{
            println("received $it")
        }
    }
    //resetReplayCache : 값을 저장한 캐시를 초기화
    mutableSharedFlow.resetReplayCache()
    println(mutableSharedFlow.replayCache)
}

MutableSharedFlow

  • MutableSharedFlow는 SharedFlow와 FlowCollector를 모두 상속합니다.
    • SharedFlow는 Flow를 상속하고 감지하는 목적으로 활용됩니다.
    • FlowCollector는 값을 내보내는 목적으로 사용됩니다.

shareIn

  • 다양한 클래스가 변화를 감지하는 상황에서 하나의 플로우로 여러 개의 플로우를 만드는 경우 SharedFlow를 활용할 수 있습니다.
  • Flow를 SharedFlow로 바꾸는 과정에서 shareIn을 활용할 수 있습니다.
// SharingStarted.Eagerly: 즉시 값을 감지, 전송
// SharingStarted.Lazily: 첫 구독자가 나올 때 감지
// SharingStarted.WhileSubscribed: 첫 구독자가 나올 때 감지
//    - 마지막 구독자가 사라지면 플로우 중지
suspend fun main(): Unit = coroutineScope {
    val flow = flowOf("A", "B", "C")
        .onEach { delay(1000) }
    val sharedFlow: SharedFlow<String> = flow.shareIn(
        scope = this, // 플로우의 원소를 모으는 코루틴 시작
        started = SharingStarted.Eagerly
    )

    // 총 3회 수집
    delay(500)
    launch {
        sharedFlow.collect {
            println("#1 $it")
        }
    }
    // 총 2회 수집
    delay(1000)
    launch {
        sharedFlow.collect {
            println("#2 $it")
        }
    }
    // 총 1회 수집
    delay(1000)
    launch {
        sharedFlow.collect {
            println("#3 $it")
        }
    }
}

StateFlow

  • 상태플로우는 공유플로우의 개념을 확장시킨 것입니다.
  • replay = 1인 공유플로우와 비슷하게 작동합니다.
  • value 프로퍼티로 접근 가능한 값 하나를 항상 가지고 있습니다.
suspend fun main(): Unit = coroutineScope {
    val state = MutableStateFlow("A")
    launch {
        state.collect{
            println(it)
        }
    }

    delay(100)
    // 상태 변화 시 수집하므로, B는 2번 출력
    state.value = "B"
    launch {
        state.collect{
            println(it)
        }
    }
}
  • 라이브데이터를 대체하기 위해 지원하며, 안드로이드 의존성이 없다는 특징이 있습니다.
  • 코루틴을 지원하며, 초기값을 가지기 때문에 null Safe합니다.
  • 데이터가 덮어 씌우쥐기 대문에, 관찰이 늘어날 경우 상태의 중간 변화를 받을 수 없는 경우가 있습니다.
    • 모든 이벤트가 필요할 경우 공유 플로우를 활용해야 합니다.

stateIn

  • stateIn은 Flow를 StateFlow로 변환합니다.
  • shareIn과 마찬가지로 중단 함수이며, StateFlow는 항상 값을 가져야 합니다.
suspend fun main(): Unit = coroutineScope {
    val flow = flowOf("a","b","c")
        .onEach { delay(1000) }
    val stateFlow : StateFlow<String> = flow.stateIn(this)

    stateFlow.collect {
        println(it)
    }
}

  • 하나의 데이터 소스에서 값이 변경된 것을 감지하는 경우 주로 활용합니다.