개요
저번에는 Flow에 대한 간단한 정리 를 했다. flow 정리
Flow는 비동기 데이터 스트림을 처리하기 위한 Kotlin의 API로 코루틴은 하나의 suspend 함수에서 값을 하나만 return해주지만 Flow의 경우 지속적으로 나오는 여러 개의 데이터를 처리하기 적합하다.
Flow에는 StateFlow와 SharedFlow라는 애들이 있다. 기존에 Flow의 경우 소비자가 구독을 할 때 값을 방출하는 Cold Stream이고, StateFlow와 SharedFlow는 하나 이상의 소비자들이 구독할 수 있고, 모든 구독자에게 같은 데이터를 발행하며, 구독자가 없는 경우에도 데이터를 발행하는 Hot Stream이다.
이제 StateFlow와 SharedFlow에 대해 알아보자
StateFlow
위의 코드와 같이 StateFlow는 SharedFlow를 구체화한 것으로, UI 상태를 저장하고 공유할 수 있는 flow의 한 종류이다.
얘가 나왔기 때문에 굳이 LiveData를 사용할 필요 없이, 오히려 LiveData보다 이점이 많다.
LiveData 대신 StateFlow?
1. 일단 LiveData의 경우 Domain Layer에서 사용할 수 없다.
클린 아키텍처 기준으로 Domain Layer의 경우 안드로이드에 의존성을 가지지 않은 순수 Java 및 Kotlin 코드로만 구성해야 하는데, LiveData는 androidX에 포함되어 있어서 얘때문에 Domain에서 안드로이드 의존성을 가져야 한다.
-> 하지만 StateFlow의 경우 kotlin API이기 때문에 사용이 가능해진다.
2. StateFlow가 LiveData를 대신할 수 있다.
StateFlow는 LiveData와 같이 UI 상태를 저장하고 Observe할 수 있다. 위의 나온 단점 + 대체가능이라면 StateFlow를 쓰지 않을 이유는 없지 않을까?
3. 쓰레드 안전
StateFlow는 데이터 업데이트와 구독을 다중 스레드 환경에서도 안전하게 할 수 있어 비동기 처리에 있어서 매우 중요한 이점을 가지고 있다. 반면 LiveData는 주로 UI 스레드에서 동작하도록 설계되어 비동기 작업에서 주의가 필요하다고 한다
Flow 대신 StateFlow & SharedFlow ?
1. Stream에 비해 더 적은 메모리의 사용
Flow(=Cold Stream)의 경우 새로운 구독자가 생길 때마다 데이터를 다시 방출하기 때문에 메모리 사용량이 늘어난다.
반면에 StateFlow(=Hot Stream)의 경우는 여러 구독자들이 하나의 공유 스트림을 사용하기 때문에, 데이터를 방출 시 모든 구독자에게 방출된다. 따라서 구독자마다 개별적인 스트림이 생성되지 않아 메모리 사용이 효율적이라고 한다.
2. StateFlow와 SharedFlow 의 경우 수명 주기를 자동으로 처리함.
Flow의 경우 수동으로 수명 주기를 관리해야 한다. 반면, StateFlow와 SharedFlow는 사용되는 컴포넌트의 생명주기에 적절하게 사용할 수 있다.
StateFlow 특징
1) StateFlow는 값을 가지고 있을 수 있어 상태 관리에 용이하다. => ViewModel에서 주로 사용함.
2) StateFlow는 초기값을 가진다.
private val _uiState = MutableStateFlow(0) // 초기값을 0으로 설정
val uiState: StateFlow<Int> = _uiState.asStateFlow()
fun incrementState() {
_uiState.value += 1
}
위의 코드와 같이 값을 저장할 수 있다.
이때 MutableStateFlow는 읽고 쓸 수 있는 mutable
StateFlow는 읽을 수는 있지만 쓸 수는 없는 immutable이다.
3) StateFlow는 1의 고정된 replayCache값을 가져, 구독자들에게 가장 최근의 데이터를 방출한다.
replayCache는 현재 StateFlow에 저장된 값을 리스트로 제공하는데, 1이라는 것은 하나의 값만 가질 수 있다는 것으로 즉 항상 최신 상태를 가지고 있다는 것을 의미한다.
SharedFlow
SharedFlow는 이벤트를 관리하는 데 적합한 Hot Stream이다. StateFlow와의 차이점은 값을 항상 가지고 있지 않아, 이벤트가 발생했을 때 이를 구독자에게 알리는데 적합하다.
SharedFlow 특징
1) SharedFlow의 replayCache의 기본값은 0
SharedFlow의 replayCache는 StateFlow와 달리 직접 정의 할 수 있고, 기본값은 0이다. 기본값 사용시는 구성 변경시 기존 구독자 뿐 아니라 새 구독자에게도 값이 방출되지 않는다.
2) SharedFlow의 이벤트 트리거
개인적인 생각에서 SharedFlow의 이 기능이 너무 좋다. 동일한 이벤트에 대한 처리가 가능하다. 나의 경우 LiveData를 동일한 이벤트 처리에 사용했을 때, 다시 값을 바꿔주고 처리했어야 했다. 하지만 SharedFlow가 있어 동일한 이벤트에 대한 처리가 가능하다. 왜냐하면 동일한 값을 재방출할 수 있으니까!
sealed class EventState{
data object Empty : EventState
data object SnackBar : EventState
data object ToastMessage : EventState
}
private val _events = MutableSharedFlow<EventState>()
val events: SharedFlow<EventState> = _events.asSharedFlow()
suspend fun triggerEvent(event: EventState) {
_events.emit(event)
}
정리
특징 | StateFlow | SharedFlow |
저장 여부 | 마지막 상태 저장 | 상태 저장 X |
용도 | UI 상태 관리 | 이벤트 관리 |
Replay 설정 | 불가 | 가능 |
나만의 팁!
flowWithLifecycle
flow가 수집될 때 적절한 생명 주기 상태에서만 수집하도록 해야 메모리 누수나 불필요한 작업을 방지할 수 있는데
나는 flowWithLifecycle를 사용한다. repeatOnLifecycle이라는 것도 있는데 이건 다음에 알아봐야지.
기능 및 동작 방식
특정 Lifecycle 상태에서만 Flow를 수집하도록 제한한다. 예를 들어 STARTED 또는 RESUMED 상태에서만 Flow를 활성해 데이터를 처리하고, PAUSED나 STOPPED 상태에서는 데이터를 수집하지 않도록 할 수 있다.
viewModel.event
.flowWithLifecycle(viewLifecycleOwner.lifecycle)
.onEach { value ->
// 데이터를 처리하는 로직
}.launchIn(viewLifecycleOwner.lifecycleScope)
나의 경우 주로 collect가 아닌 onEach와 launchIn을 사용해서 수집할 때 보통 위의 코드와 같이 쓴다.
장점
1. 메모리 누수 방지
화면이 보이지 않으면 데이터 수집을 중지하기 때문에 메모리 누수를 방지할 수 있다.
2. 리소스 절약
불필요한 데이터 수집이나 계산을 피할 수 있어 앱 성능을 향상시킨다.
3. 생명 주기 안전
수동으로 생명 주기를 관리할 필요 없이 자동으로 UI 상태에 따라 데이터 수집이 조절된다.
이제 LiveData를 한번 보내주고, Flow를 써보는건 어떨까요?!
계속해서 부족한 내용들은 보충해보겠습니다요
'Language > kotlin' 카테고리의 다른 글
[Kotlin] Enum 클래스 (0) | 2025.02.05 |
---|---|
[Kotlin] Channel (3) | 2024.11.07 |
[Kotlin] Flow - 소개 및 연산자 (8) | 2024.09.29 |
[Kotlin] 고차 함수와 람다 표현식 (0) | 2024.08.10 |
[Kotlin] object와 companion object의 초기화 시점 (0) | 2024.08.09 |