StateFlow vs SharedFlow
코루틴 플로우에 대해서 공부하다보면 Android 개발자라면 꽤 익숙한 이름들을 발견할 수 있다.
바로 StateFlow
와 SharedFlow
이다.
이 두 가지는 모두 Hot Stream에 속하는 녀석이다.
각각 어떤 녀석인지 한 번 알아보자.
1. StateFlow
StateFlow
는 문자 그대로 현재 상태를 표현하는 flow로 SharedFlow
의 일종이다.
“상태”를 표현하기때문에 생성시 무조건 초기값이 필요하고, 하나의 업데이트 가능한 값을 가지는 게 특징이다.
상술했듯 StateFlow
는 소비자의 존재와는 별개로 독립적으로 활성화된 객체를 가지고 있기 때문에 Hot Stream으로 분류된다.
매우 중요한 특징으로 StateFlow
는 절대 완료되지않는다.
collect
함수를 호출하더라도 정상적으로 종료되지않고, launchIn
함수를 호출하여 시작된 코루틴도 종료되지않는다.
따라서 StateFlow
에서 갑을 수집하는 녀석을 구독자(subscriber) 라고 부른다.
1.1 StateFlow의 생성과 상태 접근
StateFlow
는 MutableStateFlow
클래스의 생성자로 만들 수 있다.
1 | val stateFlow = MutableStateFlow(0) |
상태의 접근은 기본 발행 함수인 emit
을 쓸 수도 있지만 value
프로퍼티를 통해 접근할 수도 있다.
1 | val stateFlow = MutableStateFlow(0) |
모든 상태에 대한 변경은 모두 통합되므로 모든 구독자는 가장 최근값을 획득할 수 있다.
이처럼 상태 관리에 특화된 flow이기 때문에 모든 종류의 상태를 표현하는 data-model 클래스로 적합하며, combine
등의 함수를 통해 여러 StateFlow
의 값을 결합하는 등의 응용이 가능하다.
1.2. StateFlow 예제
아래 예제는 특정한 정수를 상태로 가지는 StateFlow
를 캡슐화한 CouterModel
클래스이다.
이 클래스는 inc()
함수가 호출될 때마다 count
값을 1씩 증가시킨다.
1 | class CounterModel { |
1.3. 강력한 평등기반 결합(Strong equality-based conflation)
StateFlow
의 value
는 Any.equals
비교를 사용하여 결합되므로, 이전에 내보낸 값과 동일한 값을 소비자에게 송신하지않도록 처리된다.
바꿔말해 Any.equals
의 규칙대로 작성되지않은 클래스에서는 StateFlow
가 의도대로 동작하지 않을 수 있다.
1.4. StateFlow는 SharedFlow이다. (State flow is a shared flow)
StateFlow
는 상술했듯 SharedFlow
의 한 종류로, 상태를 공유하기 위한 특수 목적의 구현체이다.
따라서 SharedFlow
의 모든 기본 규칙, 제약 조건, 연산자 등은 StateFlow
로 그대로 계승된다.
다만 초기값을 가지는 것, 단 한 개의 최신 상태값을 가지는 것 그리고 SharedFlow
의 resetReplayCache
가 지원되지않는 것이 그 차이점이다.
SharedFlow
를 StateFlow
처럼 쓰려면 아래와 같이 작성하여 사용할 수 있다.
1 | // MutableStateFlow(initialValue) is a shared flow with the following parameters: |
결론적으로 StateFlow
는 객체의 최신 상태를 얻어오기위한 특수목적 이외에는 SharedFlow
를 사용하는 것이 권장된다.
이 특수목적 이외에는 추가적인 버퍼링 작업, 초기값의 생략, 부가적인 값이 해당된다.
1.5. 동시성(Concurrency)
StateFlow
의 모든 메서드는 모두 thread-safe 하며, 외부의 동기화없이도 복수개의 코루틴에서 안전하게 호출할 수 있다.
1.6. StateFlow를 구현하면서..
StateFlow
는 메모리의 점유를 통한 소비와 자유로운 할당에 최적화되어있다.
상술했듯 thread-safe한 호출을 보장하기 위해 내부적으로 lock-unlock을 수행하지만 코루틴 내에서 별도의 dead-locks은 발생하지않도록 처리되어있다.
신규 구독자의 추가시 각각 O(1)
의 시간 복잡도를 가지며, 상태값의 갱신의 경우 현재 활성화된 구독자의 수(=N) 에 비례한 O(N)
시간 복잡도를 가진다.