๊นจ์•Œ ๊ฐœ๋… ๐Ÿ“‘/Kotlin

[Kotlin] Coroutines Flow - Flow.combine()๊ณผ Flow.stateIn()

interfacer_han 2024. 8. 29. 19:35

#1 ๊ฐœ์š”

 

combine

Returns a Flow whose values are generated with transform function by combining the most recently emitted values by each flow. It can be demonstrated with the following example: val flow = flowOf(1, 2).onEach { delay(10) }val flow2 = flowOf("a", "b", "c").o

kotlinlang.org

 

stateIn

Converts a cold Flow into a hot StateFlow that is started in the given coroutine scope, sharing the most recently emitted value from a single running instance of the upstream flow with multiple downstream subscribers. See the StateFlow documentation for th

kotlinlang.org

Flow์˜ ํ™•์žฅ ํ•จ์ˆ˜์ธ Flow.combine() ๋ฐ Flow.stateIn()์„ ์‚ดํŽด๋ณธ๋‹ค. 
 

#2 Flow.combine()

#2-1 ๊ฐœ์š”

Flow.combine()์€ 2๊ฐœ ์ด์ƒ์˜ Flow๋ฅผ ์กฐํ•ฉ(Combine)ํ•˜์—ฌ ์ƒˆ๋กœ์šด Flow๋ฅผ ๋งŒ๋“œ๋Š” ํ™•์žฅํ•จ์ˆ˜๋‹ค.

 

#2-2 ์กฐํ•ฉ(combine)ํ•  ์žฌ๋ฃŒ๊ฐ€ ์ „๋ถ€ Cold Flow์ธ ๊ฒฝ์šฐ

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flow

// ์˜จ๋„ Flow
fun tempFlow(): Flow<Int> = flow {
    for (i in 33 downTo 26) {
        delay(1000)
        emit(i)
    }
}

// ๊ตฌ๋ฆ„ Flow
fun cloudFlow(): Flow<String> = flow {
    val cloudList = arrayListOf("๋ง‘์Œ", "ํ๋ฆผ", "๋จน๊ตฌ๋ฆ„", "๋ง‘์Œ")

    for (cloud in cloudList) {
        delay(3000)
        emit(cloud)
    }
}

// ์Šต๋„ Flow
fun humFlow(): Flow<Int> = flow {
    for (i in 40..70 step 5) {
        delay(2000)
        emit(i)
    }
}

fun main() {
    val combinedFlow = combine(tempFlow(), cloudFlow(), humFlow()) { temp, cloud, hum ->
        "์˜จ๋„: $temp°C, ๊ตฌ๋ฆ„: $cloud, ์Šต๋„: $hum%"
    }

    CoroutineScope(Dispatchers.Default).launch {
        var printCount = 0

        combinedFlow.collect { result ->
            println("(${++printCount}) $result")
        }
    }

    runBlocking {
        delay(20000)
    }
}

/* ↑ ↑ ↑ ์ถœ๋ ฅ ๊ฒฐ๊ณผ
(1) ์˜จ๋„: 32°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 40%
(2) ์˜จ๋„: 31°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 40%
(3) ์˜จ๋„: 31°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 45%
(4) ์˜จ๋„: 30°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 45%
(5) ์˜จ๋„: 29°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 45%
(6) ์˜จ๋„: 29°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 50%
(7) ์˜จ๋„: 28°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 50%
(8) ์˜จ๋„: 27°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 50%
(9) ์˜จ๋„: 27°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 55%
(10) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 55%
(11) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋จน๊ตฌ๋ฆ„, ์Šต๋„: 55%
(12) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋จน๊ตฌ๋ฆ„, ์Šต๋„: 60%
(13) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 60%
(14) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 65%
(15) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 70%
*/

tempFlow, cloudFlow, humFlow๋ฅผ ์กฐํ•ฉํ•ด combinedFlow๋ผ๋Š” ์ƒˆ๋กœ์šด Flow๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. combinedFlow๊ฐ€ emit()ํ•˜๋Š” ๋ถ€๋ถ„์„ ๋ณด๋ฉด, 3๊ฐœ์˜ Flow๋ฅผ ์žฌ๋ฃŒ๋กœ ๋งŒ๋“ค์—ˆ๊ธฐ์— 3๊ฐœ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜(temp, cloud, hum)๊ฐ€ ์‚ฌ์šฉ๋œ ๋ชจ์Šต์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์ด๋•Œ, tempFlow๋Š” ์ด 8๊ฐœ์˜ ๊ฐ’์„ emit()ํ•˜๊ณ  cloudFlow๋Š” 4๊ฐœ, humFlow๋Š” 7๊ฐœ์˜ ๊ฐ’์„ emit()ํ•œ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด,  combinedFlow๋Š” ์ด 8 × 4 × 7 = 224๊ฐœ์˜ ๊ฐ’์„ emit()ํ•˜๊ฒŒ ๋ ๊นŒ? ๊ทธ๋ ‡์ง€ ์•Š๋‹ค. combinedFlow๋Š” ์žฌ๋ฃŒ๋กœ ์‚ฌ์šฉํ•œ Flow๋“ค์˜ ๋ชจ๋“  ์กฐํ•ฉ์˜ ๊ฒฝ์šฐ์˜ ์ˆ˜๋ฅผ ๋„์ถœํ•˜๋Š” ๋ชฉ์ ์ด ์•„๋‹ˆ๋ผ, ์ตœ์‹  ๊ฐ’๋“ค์˜ ์กฐํ•ฉ์„ ์ตœ๋Œ€ํ•œ ๋นจ๋ฆฌ ํ‘œ์‹œํ•˜๋Š”๋ฐ ๊ทธ ๋ชฉ์ ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ์žฌ๋ฃŒ๋กœ ์‚ฌ์šฉ๋œ Flow๋“ค ๊ฐ๊ฐ์˜ emit() ์‹œ์ ์ด ๋น„์Šทํ•œ ๊ฒฝ์šฐ emit()๋œ ๊ฐ’์ด ๋ฌด์‹œ๋˜๋Š” ๊ฒฝ์šฐ๋„ ์ƒ๊ธด๋‹ค. ๊ทธ๋ž˜์„œ ์œ„ ์ฝ”๋“œ์˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ์˜ ๊ฐฏ์ˆ˜๋„ 15๊ฐœ์ธ ๊ฒƒ์ด๋‹ค.

#2-3 ์กฐํ•ฉ(combine)ํ•  ์žฌ๋ฃŒ์— Hot Flow๋„ ์žˆ๋Š” ๊ฒฝ์šฐ (SharedFlow)

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

// ์˜จ๋„ Flow
fun tempFlow(): Flow<Int> = flow {
    for (i in 33 downTo 26) {
        delay(1000)
        emit(i)
    }
}

// ๊ตฌ๋ฆ„ Flow
fun cloudFlow() = MutableSharedFlow<String>()

// ์Šต๋„ Flow
fun humFlow(): Flow<Int> = flow {
    for (i in 40..70 step 5) {
        delay(2000)
        emit(i)
    }
}

fun main() {
    val cloudFlowInstance = cloudFlow()
    val combinedFlow = combine(tempFlow(), cloudFlowInstance, humFlow()) { temp, cloud, hum ->
        "์˜จ๋„: $temp°C, ๊ตฌ๋ฆ„: $cloud, ์Šต๋„: $hum%"
    }

    CoroutineScope(Dispatchers.Default).launch {
        var printCount = 0

        combinedFlow.collect { result ->
            println("(${++printCount}) $result")
        }
    }

    CoroutineScope(Dispatchers.Default).launch {
        val cloudList = arrayListOf("๋ง‘์Œ", "ํ๋ฆผ", "๋จน๊ตฌ๋ฆ„", "๋ง‘์Œ")

        for (cloud in cloudList) {
            delay(3000)
            cloudFlowInstance.emit(cloud)
        }
    }

    runBlocking {
        delay(20000)
    }
}

/* ↑ ↑ ↑ ์ถœ๋ ฅ ๊ฒฐ๊ณผ
(1) ์˜จ๋„: 32°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 40%
(2) ์˜จ๋„: 31°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 40%
(3) ์˜จ๋„: 31°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 45%
(4) ์˜จ๋„: 30°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 45%
(5) ์˜จ๋„: 29°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 45%
(6) ์˜จ๋„: 29°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 45%
(7) ์˜จ๋„: 29°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 50%
(8) ์˜จ๋„: 28°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 50%
(9) ์˜จ๋„: 27°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 50%
(10) ์˜จ๋„: 27°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 55%
(11) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 55%
(12) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋จน๊ตฌ๋ฆ„, ์Šต๋„: 55%
(13) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋จน๊ตฌ๋ฆ„, ์Šต๋„: 60%
(14) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 60%
(15) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 65%
(16) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 70%
*/

#2-2์˜ ์ฝ”๋“œ ์† cloudFlow๋ฅผ Hot Flow๋กœ ๋ณ€๊ฒฝํ–ˆ๋‹ค. ์ถœ๋ ฅ ๊ฒฐ๊ณผ์— ํฌ๊ฒŒ ๋‹ฌ๋ผ์ง„ ์ ์€ ์—†๋‹ค. ์ถœ๋ ฅ ๊ฒฐ๊ณผ์˜ ๊ฐฏ์ˆ˜๊ฐ€ 15๊ฐœ ์—์„œ 16๊ฐœ๋กœ ๋ณ€๊ฒฝ๋˜๊ธด ํ–ˆ์ง€๋งŒ, ์ด๋Š” ์• ์ดˆ์— ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ๋ฐ˜๋ณตํ•ด์„œ ์‹คํ–‰ํ•ด๋„ 1 ~ 2๊ฐœ์”ฉ ๋‹ค๋ฅด๋‹ค.

 

#2-4 Hot Flow์— ์ดˆ๊นƒ๊ฐ’์„ ๋Šฆ๊ฒŒ ์ค€๋‹ค๋ฉด? (SharedFlow)

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

// ์˜จ๋„ Flow
fun tempFlow(): Flow<Int> = flow {
    for (i in 33 downTo 26) {
        delay(1000)
        emit(i)
    }
}

// ๊ตฌ๋ฆ„ Flow
fun cloudFlow() = MutableSharedFlow<String>()

// ์Šต๋„ Flow
fun humFlow(): Flow<Int> = flow {
    for (i in 40..70 step 5) {
        delay(2000)
        emit(i)
    }
}

fun main() {
    val cloudFlowInstance = cloudFlow()
    val combinedFlow = combine(tempFlow(), cloudFlowInstance, humFlow()) { temp, cloud, hum ->
        "์˜จ๋„: $temp°C, ๊ตฌ๋ฆ„: $cloud, ์Šต๋„: $hum%"
    }

    CoroutineScope(Dispatchers.Default).launch {
        var printCount = 0

        combinedFlow.collect { result ->
            println("(${++printCount}) $result")
        }
    }

    runBlocking {
        delay(10000) // 10์ดˆ
        println("์ดˆ๊นƒ๊ฐ’์„ ๋ณด์œ ํ•˜์ง€ ์•Š์€ Flow๋ฅผ ์žฌ๋ฃŒ๋กœ combine()ํ•˜๋ฉด ํ•ด๋‹น Flow์— ๊ฐ’์ด ๋“ค์–ด์˜ฌ ๋•Œ๊นŒ์ง€ collect()๊ฐ€ ์ˆ˜ํ–‰๋˜์ง€ ์•Š์Œ")
    }

    CoroutineScope(Dispatchers.Default).launch {
        val cloudList = arrayListOf("๋ง‘์Œ", "ํ๋ฆผ", "๋จน๊ตฌ๋ฆ„", "๋ง‘์Œ")

        for (cloud in cloudList) {
            delay(3000)
            cloudFlowInstance.emit(cloud)
        }
    }

    runBlocking {
        delay(20000)
    }
}

/* ↑ ↑ ↑ ์ถœ๋ ฅ ๊ฒฐ๊ณผ
์ดˆ๊นƒ๊ฐ’์„ ๋ณด์œ ํ•˜์ง€ ์•Š์€ Flow๋ฅผ ์žฌ๋ฃŒ๋กœ combine()ํ•˜๋ฉด ํ•ด๋‹น Flow์— ๊ฐ’์ด ๋“ค์–ด์˜ฌ ๋•Œ๊นŒ์ง€ collect()๊ฐ€ ์ˆ˜ํ–‰๋˜์ง€ ์•Š์Œ
(1) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 65%
(2) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 70%
(3) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 70%
(4) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋จน๊ตฌ๋ฆ„, ์Šต๋„: 70%
(5) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 70%
*/

#2-3์™€ ๊ฐ™์€ ์ฝ”๋“œ์ง€๋งŒ, collecter๋ฅผ ๋ถ™์ด๊ณ  ๋‚˜์„œ 10์ดˆ ๋’ค์— Hot Flow๊ฐ€ ๊ฐ’์„ emit()ํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค. ์ด๋Ÿฌ๋ฉด combinedFlow๋Š” ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ• ๊นŒ? ์ฆ‰, combinedFlow๋Š” ๊ทธ ์žฌ๋ฃŒ Flow์ธ cloudFlow๊ฐ€ 10์ดˆ ๋™์•ˆ ์ดˆ๊นƒ๊ฐ’์„ ๋ณด์œ ํ•˜์ง€ ์•Š์€ ์ƒํƒœ๋กœ ๋ฐฉ์น˜๋˜๋Š” ๊ฒƒ์ด๋‹ค. SharedFlow์ธ cloudFlow๊ฐ€ ๊ฐ’์„ ๋ฐฉ์ถœํ•˜๊ธฐ ์ „๊นŒ์ง€ combinedFlow.collect()๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š๋‹ค๊ฐ€, cloudFlow๊ฐ€ ๊ฐ’์„ ์ถœ๋ ฅํ•˜๋ฉด ๋น„๋กœ์†Œ ์‹คํ–‰๋œ๋‹ค.

 

์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด, collector๋ฅผ ๋ถ™์ธ ์‹œ์ ์—, cloudFlow๋ฅผ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€ Flow๋“ค์€ ์ด๋ฏธ ๊ฐ’์„ emit()ํ•˜๊ณ  ์žˆ์—ˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด tempFlow๋Š” 32°C๋ถ€ํ„ฐ ๊ฐ’์„ emit()ํ•˜๋Š”๋ฐ, ์ถœ๋ ฅ ๊ฒฐ๊ณผ์—๋Š” ํ•ด๋‹น ์˜จ๋„๊ฐ€ ๋ณด์ด์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. cloudFlow๊ฐ€ 10์ดˆ ๋™์•ˆ ๊ฐ’์„ emit()ํ•˜์ง€ ์•Š๋Š” ๋™์•ˆ ์˜จ๋„๊ฐ€ 26°C๊นŒ์ง€ ๋‚ด๋ ค๊ฐ„ ๊ฒƒ์ด๋‹ค.

 

#2-5 ์ดˆ๊นƒ๊ฐ’์ด ํ•ญ์ƒ ์กด์žฌํ•˜๋Š” Hot Flow๋ผ๋ฉด? (StateFlow)

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

// ์˜จ๋„ Flow
fun tempFlow(): Flow<Int> = flow {
    for (i in 33 downTo 26) {
        delay(1000)
        emit(i)
    }
}

// ๊ตฌ๋ฆ„ Flow
fun cloudFlow() = MutableStateFlow("๊ตฌ๋ฆ„ ์ •๋ณด ์—†์Œ (์ดˆ๊นƒ๊ฐ’)")

// ์Šต๋„ Flow
fun humFlow(): Flow<Int> = flow {
    for (i in 40..70 step 5) {
        delay(2000)
        emit(i)
    }
}

fun main() {
    val cloudFlowInstance = cloudFlow()
    val combinedFlow = combine(tempFlow(), cloudFlowInstance, humFlow()) { temp, cloud, hum ->
        "์˜จ๋„: $temp°C, ๊ตฌ๋ฆ„: $cloud, ์Šต๋„: $hum%"
    }

    CoroutineScope(Dispatchers.Default).launch {
        var printCount = 0

        combinedFlow.collect { result ->
            println("(${++printCount}) $result")
        }
    }

    CoroutineScope(Dispatchers.Default).launch {
        val cloudList = arrayListOf("๋ง‘์Œ", "ํ๋ฆผ", "๋จน๊ตฌ๋ฆ„", "๋ง‘์Œ")

        for (cloud in cloudList) {
            delay(3000)
            cloudFlowInstance.emit(cloud)
        }
    }

    runBlocking {
        delay(20000)
    }
}

/* ↑ ↑ ↑ ์ถœ๋ ฅ ๊ฒฐ๊ณผ
(1) ์˜จ๋„: 33°C, ๊ตฌ๋ฆ„: ๊ตฌ๋ฆ„ ์ •๋ณด ์—†์Œ (์ดˆ๊นƒ๊ฐ’), ์Šต๋„: 40%
(2) ์˜จ๋„: 32°C, ๊ตฌ๋ฆ„: ๊ตฌ๋ฆ„ ์ •๋ณด ์—†์Œ (์ดˆ๊นƒ๊ฐ’), ์Šต๋„: 40%
(3) ์˜จ๋„: 32°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 40%
(4) ์˜จ๋„: 31°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 40%
(5) ์˜จ๋„: 31°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 45%
(6) ์˜จ๋„: 30°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 45%
(7) ์˜จ๋„: 29°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 45%
(8) ์˜จ๋„: 29°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 45%
(9) ์˜จ๋„: 29°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 50%
(10) ์˜จ๋„: 28°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 50%
(11) ์˜จ๋„: 27°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 50%
(12) ์˜จ๋„: 27°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 55%
(13) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 55%
(14) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋จน๊ตฌ๋ฆ„, ์Šต๋„: 55%
(15) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋จน๊ตฌ๋ฆ„, ์Šต๋„: 60%
(16) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 60%
(17) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 65%
(18) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 70%
*/

๊ทธ๋ ‡๋‹ค๋ฉด, cloudFlow๊ฐ€ SharedFlow๊ฐ€ ์•„๋‹Œ, Hot Flow์˜ ๋˜๋‹ค๋ฅธ ํ˜•ํƒœ์ธ StateFlow์˜€๋‹ค๋ฉด? ์ด ๊ฒฝ์šฐ๋Š” combindFlow๊ฐ€ ๋Œ€๊ธฐํ•˜๋Š” ์ผ์ด ์—†๋‹ค. ์™œ๋ƒํ•˜๋ฉด, StateFlow๊ฐ€ ์ง€๋‹Œ ์ดˆ๊นƒ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์‹ค์ œ๋กœ, StateFlow๋Š” ๋ฆฌ์Šค๋„ˆ(collector)๊ฐ€ ์ƒ๊ธฐ๋Š” ์ฆ‰์‹œ ์ž์‹ ์ด ๋ณด์œ ํ•œ ๊ฐ’(StateFlow.value)๋ฅผ ํ•ด๋‹น ๋ฆฌ์Šค๋„ˆ์—๊ฒŒ ์ „๋‹ฌํ•œ๋‹ค. ์ฆ‰, ์ด๋ฏธ ๋ˆ„๊ตฌ๋ณด๋‹ค ๋น ๋ฅด๊ฒŒ emit()์ด ๋œ๋‹ค๋Š” ์–˜๊ธฐ๋‹ค.

 

#3 Flow.stateIn()

#3-1 ๊ฐœ์š”

fun <T> Flow<T>.stateIn(
    scope: CoroutineScope, 
    started: SharingStarted, 
    initialValue: T
): StateFlow<T>

Flow.stateIn()์€ Flow๋ฅผ StateFlow๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค. StateFlow๋Š” ์ดˆ๊นƒ๊ฐ’์ด ์กด์žฌํ•˜๋Š” ๊ตฌ์กฐ์ด๋ฏ€๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜์— initialValue๊ฐ€ ์กด์žฌํ•˜๋Š” ๊ฒƒ๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ์•ˆ๋“œ๋กœ์ด๋“œ Jetpack Compose์—์„œ stateIn()์€ combine()๊ณผ ๋‹จ์ง์ด๋‹ค. combine()๋œ Flow๋ฅผ StateFlow๋กœ ๋งŒ๋“ค์–ด, Jetpack Compose์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
 

#3-2 ๊ธฐ๋ณธ์ ์ธ ์ฝ”๋“œ

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

// ์˜จ๋„ Flow
fun tempFlow(): Flow<Int> = flow {
    for (i in 33 downTo 26) {
        delay(1000)
        emit(i)
    }
}

// ๊ตฌ๋ฆ„ Flow
fun cloudFlow(): Flow<String> = flow {
    val cloudList = arrayListOf("๋ง‘์Œ", "ํ๋ฆผ", "๋จน๊ตฌ๋ฆ„", "๋ง‘์Œ")

    for (cloud in cloudList) {
        delay(3000)
        emit(cloud)
    }
}

// ์Šต๋„ Flow
fun humFlow(): Flow<Int> = flow {
    for (i in 40..70 step 5) {
        delay(2000)
        emit(i)
    }
}

fun main() {
    val combinedFlow = combine(tempFlow(), cloudFlow(), humFlow()) { temp, cloud, hum ->
        "์˜จ๋„: $temp°C, ๊ตฌ๋ฆ„: $cloud, ์Šต๋„: $hum%"
    }

    val stateFlow = combinedFlow.stateIn(
        scope = CoroutineScope(Dispatchers.Default),
        started = SharingStarted.Lazily,
        initialValue = "์ดˆ๊ธฐํ™” ์ค‘..."
    )

    CoroutineScope(Dispatchers.Default).launch {
        var printCount = 0

        stateFlow.collect { result ->
            println("(${++printCount}) $result")
        }
    }

    runBlocking {
        delay(20000)
    }
}

/* ↑ ↑ ↑ ์ถœ๋ ฅ ๊ฒฐ๊ณผ
(1) ์ดˆ๊ธฐํ™” ์ค‘...
(2) ์˜จ๋„: 32°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 40%
(3) ์˜จ๋„: 31°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 40%
(4) ์˜จ๋„: 31°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 45%
(5) ์˜จ๋„: 30°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 45%
(6) ์˜จ๋„: 29°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 45%
(7) ์˜จ๋„: 29°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 45%
(8) ์˜จ๋„: 29°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 50%
(9) ์˜จ๋„: 28°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 50%
(10) ์˜จ๋„: 27°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 50%
(11) ์˜จ๋„: 27°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 55%
(12) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ํ๋ฆผ, ์Šต๋„: 55%
(13) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋จน๊ตฌ๋ฆ„, ์Šต๋„: 55%
(14) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋จน๊ตฌ๋ฆ„, ์Šต๋„: 60%
(15) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 65%
(16) ์˜จ๋„: 26°C, ๊ตฌ๋ฆ„: ๋ง‘์Œ, ์Šต๋„: 70%
*/

๋งจ ๋จผ์ € ์ดˆ๊นƒ๊ฐ’์ด ์ถœ๋ ฅ๋œ๋‹ค. ๋‚˜๋จธ์ง€๋Š” ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋Š” ์ด์ „๊ณผ ๊ฐ™๋‹ค.

#4 ์š”์•ฝ

Flow๋ฅผ ํ•˜๋‚˜๋กœ ํ•ฉ์ณ ๋ฆฌ์Šค๋„ˆ(collector)๋ฅผ ๋ถ™์ผ ์ˆ˜ ์žˆ๋‹ค. ํ•ฉ์ณ์ง„ Flow๋ฅผ Jetpack Compose์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์ข‹์€ StateFlow๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.