-
Kotlin - Coroutine flow카테고리 없음 2024. 2. 22. 16:22
반응형 프로그래밍
[RxJava] 반응형 프로그래밍 이해하기 — 조세영의 Kotlin World
[RxJava] 반응형 프로그래밍 이해하기
예제 링크: https://github.com/seyoungcho2/RxJavaExample 경로 : example/example1 명령형 프로그래밍과 반응형 프로그래밍 명령형 프로그래밍 명령형 프로그래밍은 컴퓨터에게 무엇을 하라고 명령하는 것이다.
kotlinworld.com
개인 공부를 위한 정리 문서입니다.
모든 내용의 출처는 아래 링크입니다. 좋은 내용 감사합니다.
출처 : [Coroutine Flow] 1. Flow란 무엇인가? — 조세영의 Kotlin World
[Coroutine Flow] 1. Flow란 무엇인가?
Coroutine의 Flow는 데이터 스트림이며, 코루틴 상에서 리액티브 프로그래밍 지원 하기 위한 구성요소이다. 이를 이해하기 위해서는 먼저 리액티브(반응형) 프로그래밍이 무엇인지, 그리고 Flow가 리
kotlinworld.com
리액티브 프로그래밍에는 데이터를 발행하는 발행자가 있고 발행자는 데이터의 소비자에게 지속적으로 데이터를 전달하는 역할을 한다. 우리는 이것을 데이터 스트림이라고 한다.
코루틴에서 데이터 스트림을 구현하기 위해서는 Flow를 사용해야 한다.
flow의 핵심 구성요소 3개
Producer생산자 / Intermediary중간 연산자 / Consumer소비자
Producer 생산자
flow{} 블록 내부에서의 emit()을 통해 데이터를 생성한다.
이때의 Data source 는 주로 서버의 데이터를 REST API를 통해 가져오거나, DB에서 가져온다.
class DustREmoteDataSource( private val dustApi:DustApi ) { fun getDustInfoFlow() : FLow<List<DustInfo>> = flow { while(true) { val dustInfos = dustApi.fetchLastedDustInfo() emit(dustInfos) delay(INTERVAL_REFRESH) // 60초 마다 반복해서 데이터를 생성 } } }Imtermediary 중간 연산자
생성된 데이터를 수정한다.
Flow에서 지원하는 중간 연산자를 이용해 A객체를 B객체로 바꿀 수 있다.
map / filter / onEach 등의 중간 연산자가 있다.
class DustRepository( private val dustRemoteDataSource: DustRemoteDataSource, ) { fun getDustsInfoOfViewItem(locale: Locale) = dustRemoteDataSource.getDustInfoFlow().map{ it.filter{ this.locale == locale }} }Consumer 소비자
Android 상에서 Consumer는 주로 UI 구성요소이다.
Flow에서 collect를 이용해 전달된 데이터를 소비할 수 있다.
class DustViewModel( private val dustRepository: DustRepository ) : ViewModel() { fun collectDustInfoOf(locale: Locale) = viewModelScope.launch { dustRepository.getDustInfoOf(locale).collect { dustInfors -> /* ... */ } } }코루틴 공식문서 번역 자료
Kotlin Coroutines 공식 문서 번역을 시작하며 · GitBook (seyoungcho2.github.io)
Kotlin Coroutines 공식 문서 번역을 시작하며 · GitBook
Kotlin Coroutines 공식 문서 번역을 시작하며 Kotlin Coroutines는 Kotlin을 위한 강력한 비동기 솔루션이다. 안드로이드 실무에서는 한동안 높은 점유율을 자랑한 RxJava를 Coroutines가 대체하고 있으며, 새로
seyoungcho2.github.io
[Coroutine Flow] 2. Flow와 StateFlow의 차이는 무엇인가? — 조세영의 Kotlin World
Flow는 데이터의 흐름이다. Flow는 데이터의 흐름을 발생시키지만 할 뿐 데이터를 저장하지 않는다.
화면이 재구성될 때마다 Data를 가져오는건 비효율 적이므로 collect 한 데이터를 ViewModel에 저장해놓고 사용한다.
((ViewModel은 onDestroy가 불리더라더도 살아있다)
ViewModel이 데이터를 저장하고 있으려면 별도의 데이터 홀더 변수를 만들어야 한다.
그리고 데이터 홀더 변수는 Reactive하지 않기 때문에 UI에서 해당 데이터 홀더 변수를 구독하기 위해 별도의 fetching 로직을 만들어야 한다.
이를 위해 ViewModel에서 데이터 홀더 변수와 flow를 같이 사용한다. 이 방법이 가장 정합하다.
하지만 둘 모두 보일러 플레이트 코드(비슷한 코드가 반복됨)를 만들어 낸다는 문제가 있다.
이러한 문제를 해결하기 위해 등장한 것이 바로 StateFlow이다.
StateFlow는 데이터 홀더(저장소) 역할과 Data stream 역할을 모두 한다.
UI단에서 StateFlow를 구독해 UIState를 업데이트하면 화면이 재구성될때마다 서버에 데이터를 요청할 필요가 없어진다.
[Coroutine Flow] 3. stateIn 사용하여 Flow를 StateFlow로 변환하기 — 조세영의 Kotlin World
[Coroutine Flow] 3. stateIn 사용하여 Flow를 StateFlow로 변환하기
Flow와 StateFlow 우리는 리액티브 프로그래밍을 할 때 여러 데이터 흐름(flow)를 하나로 합쳐 하나의 데이터 흐름(Flow)으로 만들어낸다. 예를 들어 아래의 그림3에서는 Flow가 3개 있고 이것이 합쳐져
kotlinworld.com
Flow를 UI에서 사용하기 위해 StateFlow로 변환해야한다. UI는 StateFlow를 구독하여 항상 최신 데이터를 발행받는다.
StateFlow가 항상 Flow를 구독하고 있다면 메모리누수가 생기므로 StateFlow가 살아있어야 하는 Coroutine scope를 명시할 수 있어야한다.
StateFlow는 Cold stream 이 아니라 Hot Stream이다. 마지막에 홀딩하고 있는 데이터를 구독하는 구독자에게 전달할 뿐, 구독자가 구독할 때 발행을 위한 로직을 trigger하지는 않는다.
Cold Stream vs Hot Stream 이란? (tistory.com)
Cold Stream vs Hot Stream 이란?
Coroutine Cold Stream vs Hot Stream 코루틴에서 Scope와 몇 가지 연산자 등의 학습을 거쳐 flow와 channel이라는 것을 학습하게 되었는데 이 2개의 정확한 차이점은 무엇일까? stream은 정확히 무엇인가?.. 둘 다
sjoongh.tistory.com
- 마지막으로 Cold Stream 은 Lazy Stream (게으른 스트림)이라고 말할 수 있다. 누군가가 구독할 때만 값의 방출이 시작되기 때문이다. 다시 말해서 누군가가 “내가 듣고 있으니까 방출해 줘”라고 말해줘야만 시작한다는 것이다.
stateIn 사용하여 Flow를 StateFlow로 변환하기
public fun <T> Flow<T>.stateIn( scope: CoroutineScope, started: SharingStarted, initialValue: T ): StateFlow<T> { val config = configureSharing(1) val state = MutableStateFlow(initialValue) val job = scope.launchSharing(config.context, config.upstream, state, started, initialValue) return ReadonlyStateFlow(state, job) }scope : StateFlow가 Flow로 부터 데이터를 구독받을 CoroutineScope을 명시한다.
started : Flow 로부터 언제부터 구독을 할지 명시할 수 있다.
initialValue : StateFlow에 저장될 초기
Sample
val stringFlow: Flow<String> = flow { for (i in 0..1000) { emit("integer: $i") delay(1000) } }