
데이터가 변경될 때 이벤트를 발생시켜서 데이터를 계속해서 전달하도록 하는 프로그래밍 방식
기존 명령형 프로그래밍에서는 데이터의 소비자는 데이터를 요청한 후 받은 결과값을 일회성으로 수신하지만, 이러한 방식은 데이터가 필요할 때마다 결과값을 매번 요청해야 한다는 점에서 매우 비효율적임
리액티브 프로그래밍에서는 데이터를 발행하는 발행자가 있고, 소비자는 데이터의 발행자에 구독 요청을 한다
그러면 데이터의 발행자는 새로운 데이터가 들어오면 데이터의 소비자에게 지속적으로 발행한다 (데이터 스트림)
코루틴 상에서 리액티브 프로그래밍을 지원하기 위해 만들어진 구현체이다
즉, 코루틴에서 데이터 스트림을 구현하기 위해서는 Flow를 사용해야 함

위의 그림처럼 Flow의 핵심 구성요소는 Producer(생산자), Intermediary(중간 연산자), Consumer(소비자)임

생산자는 데이터를 발행하는 역할을 함
emit() or emitAll()를 통해 Flow를 생성할 수 있음 private fun getNumbers(): Flow<Int> = flow {
for (i in 1..100) {
emit(i)
println("Emit $i") // 1~100까지 방출
}
emitAll((101..200).asFlow()) // 101~200까지 방출
}
asFlow()를 이용하면 Array, Iterator, Range 등을 Flow로 생성할 수 있음(1..3).asFlow()
mapOf(Pair(3, 4)).asIterable().asFlow()
flowOf()를 통해서 Flow를 생성할 수도 있음 flowOf(1, "213", Sample(5), 3, 3.0f)

생산자가 데이터를 생성했으면 중간 연산자는 생성된 데이터를 변환함
예를 들어 생성자는 A라는 객체로 이루어진 데이터를 발행했는데, B라는 객체 데이터가 필요할 경우 Flow에서 지원하는 중간 연산자를 이용해 A객체를 B객체로 바꿀 수 있음
대표적인 중간 연산자는 map(데이터 변형), filter(데이터 필터링), onEach(모든 데이터마다 연산 수행) 등의 중간 연산자가 있음

combine() fun main() = runBlocking<Unit> {
val flowA = flowOf(1, 2, 3) // 지연 없이 1,2,3이 방출됨
val flowB = flowOf(4, 5, 6).onEach { delay(1 * 1000) } // 방출될 때마다 1초의 지연이 발생함
flowA.combine(flowB) { a, b ->
"a: $a, b: $b"
}.collect { println(it) }
}
/*
실행결과
a: 3, b: 4
a: 3, b: 5
a: 3, b: 6
*/
zip() // 3가지 아이템을 zip하기 위해서는 2번의 zip 수행
fun main() = runBlocking {
val flow1: Flow<Int> = (1..3).asFlow()
val flow2: Flow<String> = listOf("A", "B", "C").asFlow()
val flow3: Flow<Double> = listOf(1.1, 2.2, 3.3).asFlow()
flow1.zip(flow2) { value1, value2 -> Pair(value1, value2) }
.zip(flow3) { (value1, value2), value3 -> Triple(value1, value2, value3) }
.collect { value -> println("결과: $value) }
}
merge() fun main() = runBlocking<Unit> {
val flowA = flowOf(1, 2, 3)
val flowB = flowOf("a", "b", "c", "d")
flowA.map { it.toString() }
.merge(flowB)
.collect { println(it) }
}
/*
실행결과
1
a
2
b
3
c
d
*/

중간 연산자가 변환한 데이터를 변환하여 소비자로 데이터를 전달함
Flow에서는 collect를 이용해 전달된 데이터를 소비할 수 있음
안드로이드 상에서 데이터의 소비자는 보통 UI 구성요소이며, UI는 데이터를 소비하여 데이터에 맞게 UI를 그려냄
class DustViewModel(private val dustRepository: DustRepository) : ViewModel() {
fun collectDustInfoOf(locale: Locale) =
viewModelScope.launch {
dustRepository.getDustInfoOf(locale).collect { dustInfos ->
//
}
}
}
}
Flow는 코루틴을 기반으로 구현된 비동기적인 데이터 스트림 라이브러리임
안드로이드 앱에서는 Coroutine을 사용하여 비동기 작업을 수행하고, 이를 통해 백그라운드에서 실행되는 작업을 간단하게 처리할 수 있음
Flow는 데이터의 흐름을 비동기적으로 처리하는데 중점을 두며, 데이터의 변화를 관찰하고 이에 대한 반응으로 UI를 업데이트하는 데 효과적임
예를 들어, 백그라운드에서 데이터를 가져와 UI에 표시하는 작업을 할 때,
Coroutine을 사용하여 네트워크 호출이나 데이터베이스 액세스와 같은 작업을 비동기적으로 수행하고, 그 결과를 Flow로 UI에 전달하여 사용자에게 정보를 보여줄 수 있음
추가예정
https://kotlinworld.com/175
https://two22.tistory.com/16
https://developer88.tistory.com/entry/Flow-결합연산자-combine-zip-merge-비교-총정리-Kotlin-Coroutine
https://developer.android.com/codelabs/advanced-kotlin-coroutines?hl=ko#0