android, coroutine,

Flow๋ž€?

Ella Ella Follow Dec 27, 2021 ยท 5 mins read
Flow๋ž€?
Share this

๐Ÿค–

๊ฐœ๋… 9๏ธโƒฃ Coroutines Flow

Flow๋ž€ ์ˆœ์ฐจ์ ์œผ๋กœ ๊ฐ’์„ emitํ•˜๊ณ , (์ •์ƒ์ ์œผ๋กœ ๋˜๋Š” exception์„ ๋‚ด๋ฉด์„œ) complete๋˜๋Š” ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์ด๋‹ค.

interface Flow<out T>

flow์—์„œ Intermediate operators๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” (map, filter, take, zip)๊ณผ ๊ฐ™์€ ๊ฒƒ๋“ค์€ upstream flow์— ์ ์šฉ๋˜๋Š” ํ•จ์ˆ˜์ด๋ฉฐ, ๋‹ค์Œ operator๋“ค์ด ์ ์šฉ๋  ์ˆ˜ ์žˆ๋Š” downstream flow๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค. Intermediate operation์€ flow์—์„œ ํŠน์ • ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜์ง€๋„ ์•Š๊ณ , ํ•ด๋‹น operation ์ž์ฒด๊ฐ€ suspending ํ•จ์ˆ˜์ธ ๊ฒƒ๋„ ์•„๋‹ˆ๋‹ค. Intermediate operation์€ ๋ฏธ๋ž˜์˜ execution์„ ์œ„ํ•œ ์—ฐ์‡„ operation์„ ์…‹์—…ํ•ด์ฃผ๊ณ  ์žฌ๋นจ๋ฆฌ ๋ฆฌํ„ดํ•ด์ฃผ๊ธฐ๋งŒ ํ•œ๋‹ค. ์ด๋Ÿฌํ•œ ํŠน์„ฑ์ด ๋ฐ”๋กœ cold flow ํ”„๋กœํผํ‹ฐ์˜ ํŠน์„ฑ์ด๋ฉฐ, Intermediate operator๋Š” cold flow ํ”„๋กœํผํ‹ฐ์— ํ•ด๋‹นํ•œ๋‹ค.
flow์—๋Š” Terminal operators๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” suspending ํ•จ์ˆ˜๋“ค(collect, single, reduce, toList)๊ณผ launchIn ์—ฐ์‚ฐ์ž(์ฃผ์–ด์ง„ scope์—์„œ collection flow๋ฅผ ์‹œ์ž‘ํ•ด์คŒ)๊ฐ€ ์žˆ๋‹ค. Terminal operator๋Š” upstream flow์— ์ ์šฉ๋˜๊ณ , ๋ชจ๋“  operation์˜ execution์„ ํŠธ๋ฆฌ๊ฑฐํ•ด์ค€๋‹ค. flow๋ฅผ executionํ•ด์ฃผ๋Š” ๊ฒƒ์„ ๋‹ค๋ฅธ ๋ง๋กœ flow๋ฅผ collectํ•œ๋‹ค๊ณ ๋„ ํ•˜๋Š”๋ฐ, ์ด ํ–‰์œ„๋Š” ์‹ค์ œ๋กœ blocking ์—†์ด suspendingํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ˆ˜ํ–‰๋œ๋‹ค. Terminal operator๋Š” ์ •์ƒ์ ์œผ๋กœ complete๋˜๊ฑฐ๋‚˜, exeception ๋‚˜๋ฉด์„œ complete๋˜๋Š”๋ฐ, *upstream**์—์„œ ๋ชจ๋“  flow ์—ฐ์‚ฐ๋“ค์ด succesfulํ•˜๋ฉด ์ •์ƒ์ ์œผ๋กœ complete๋˜๋Š” ๊ฑฐ๊ณ  failedํ•˜๋ฉด exception๋‚˜๋ฉด์„œ complete๋œ๋‹ค. ์•ž์—์„œ ๋งํ•œ terminal operator๋“ค ์ค‘์— ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ operator๋Š” collect์ด๋‹ค.

try{
    flow.collect{ value ->
        println("Received $value")
    }
} catch (e: Exception) {
    println("the flow has thrown an exceptoin: %e")
}

๋””ํดํŠธ๋กœ flow๋“ค์€ ์ˆœ์ฐจ์ ์ด๊ณ , ๊ฐ™์€ ์ฝ”๋ฃจํ‹ด ์•ˆ์—์„œ๋Š” ๋ชจ๋“  flow ์—ฐ์‚ฐ๋“ค์€ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค. ํ•˜์ง€๋งŒ buffer์™€ flatMapMerge์™€ ๊ฐ™์ด flow์— concurrency๋ฅผ ๋„์ž…ํ•˜๋„๋ก ์„ค๊ณ„๋œ operation๋“ค์€ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.
Flow ์ธํ„ฐํŽ˜์ด์Šค๋Š” cold stream์ธ์ง€ hot stream์ธ์ง€์— ๋Œ€ํ•ด์„œ๋Š” ์–ด๋– ํ•œ ์ •๋ณด๋„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๋‹ค.
๐ŸŒŸ Cold stream์ด๋ž€?
โžก๏ธ collect๋  ๋•Œ๋งˆ๋‹ค ๋งค๋ฒˆ ๋ฐ˜๋ณต์ ์œผ๋กœ collected๋˜๊ณ  ๋™์ผํ•œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ์‹
๐ŸŒŸ Hot stream์ด๋ž€?
โžก๏ธ collect๋  ๋•Œ๋งˆ๋‹ค ๊ฐ™์€ running source๋กœ๋ถ€ํ„ฐ ๋‹ค๋ฅธ ๊ฐ’๋“ค์„ emitํ•˜๋Š” ๋ฐฉ์‹
๋ณดํ†ต flow๋Š” cold stream์ด์ง€๋งŒ, hot stream์ธ subtype(ex. SharedFlow)๋„ ์žˆ๋‹ค. flow๋Š” stateIn๊ณผ shareIn ์—ฐ์‚ฐ์ž๋ฅผ ํ†ตํ•ด hot stream์œผ๋กœ ์ „ํ™˜๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, produceIn ์—ฐ์‚ฐ์ž๋ฅผ ํ†ตํ•ด hot channel๋กœ ์ „ํ™˜๋  ์ˆ˜๋„ ์žˆ๋‹ค.

Flow builder

flow๋ฅผ ๋งŒ๋“œ๋ ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹๋“ค์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  • flowOf(โ€ฆ) ํ•จ์ˆ˜ : ์ •ํ•ด์ง„ ํฌ๊ธฐ์˜ value์˜ set์œผ๋กœ flow๋ฅผ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜
  • asFlow() ํ™•์žฅํ•จ์ˆ˜ : ๋‹ค์–‘ํ•œ ํƒ€์ž…๋“ค์„ flow๋กœ ์ „ํ™˜ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜
  • flow {โ€ฆ} ๋นŒ๋” ํ•จ์ˆ˜ : ์ˆœ์ฐจ์ ์ธ ํ˜ธ์ถœ๋˜๋˜ ๊ฒƒ์ด emit ํ•จ์ˆ˜๊ฐ€ ๋˜๋„๋ก ์ž„์˜์˜ flow๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ํ•จ์ˆ˜
  • channelFlow {โ€ฆ} ๋นŒ๋” ํ•จ์ˆ˜ : ์ž ์žฌ์ ์œผ๋กœ ๋™์‹œ ํ˜ธ์ถœ๋˜๋˜ ๊ฒƒ์ด send ํ•จ์ˆ˜๊ฐ€ ๋˜๋„๋ก ์ž„์˜์˜ flow๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ํ•จ์ˆ˜
  • MutableStateFlow & MutableSharedFlow : ๋‹ค์ด๋ ‰ํŠธ๋กœ ์—…๋ฐ์ดํŠธ๋˜๋Š” hot flow์˜ ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋“ค์„ ์ •์˜ํ•จ

Flow constraints

Flow ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ implementํ•  ๋•Œ๋Š” ์•„๋ž˜์— ๋ช…์‹œ๋œ ๋‘ ๊ฐ€์ง€์˜ ์ค‘์š”ํ•œ ํŠน์„ฑ๋“ค์„ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค.

  • Context preservation
  • Exception transparency
    ์œ„์˜ ๋‘ ํŠน์„ฑ๋“ค์€ flow์™€ ๊ด€๋ จ๋œ ์ฝ”๋“œ์— ๋Œ€ํ•œ local reasoning์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒํ•ด์ฃผ๊ณ , downstream flow collector๋“ค๋กœ๋ถ€ํ„ฐ ๋ถ„๋ฆฌ๋˜์–ด upstream flow emitter๊ฐ€ develop๋˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ฝ”๋“œ๊ฐ€ ๋ชจ๋“ˆํ™”๋˜๋„๋ก ํ•ด์ค€๋‹ค.

(1) Context preservation

flow๋Š” context preservationํ•œ ํŠน์„ฑ์„ ๊ฐ€์ง„๋‹ค. flow๋Š” flow ๊ณ ์œ ์˜ execution ์ปจํ…์ŠคํŠธ๋ฅผ ์บก์Šํ™”ํ•˜๋ฉฐ, downstream์œผ๋กœ ์ „ํŒŒ์‹œํ‚ค๊ฑฐ๋‚˜ ๋ˆ„์ถœ์‹œํ‚ค์ง€ ์•Š์œผ๋ฏ€๋กœ (ํŠน์ • transformation์ด๋‚˜ terminal operation)์˜ execution ์ปจํ…์ŠคํŠธ์— ๋Œ€ํ•ด ์ถ”๋ก ํ•˜๊ธฐ๊ฐ€ ๋” ๊ฐ„๋‹จํ•˜๋‹ค.
flow์˜ ์ปจํ…์ŠคํŠธ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์˜ค๋กœ์ง€ ํ•˜๋‚˜๋‹ค. โžก๏ธ flowOn ์—ฐ์‚ฐ์ž๋ฅผ ์“ฐ๋ฉด ๋œ๋‹ค. flowOn ์—ฐ์‚ฐ์ž๋Š” upstream context(flowOn ์—ฐ์‚ฐ์ž ์•ž์— ์žˆ๋Š” ๋ชจ๋“  ๊ฒƒ๋“ค)๋ฅผ ๋ณ€๊ฒฝ์‹œํ‚จ๋‹ค.

val flowA = flowOf(1, 2, 3)
    .map { it + 1 } // ctxA์—์„œ ์‹คํ–‰๋จ 
    .flowOn(ctxA) // upstream ์ปจํ…์ŠคํŠธ๋ฅผ ๋ฐ”๊ฟˆ: flowOf and map

// ์ด์ œ ์ปจํ…์ŠคํŠธ๋ฅผ ๋ณด์กดํ•˜๋Š” flow์ธ flowA๋ฅผ ๋งŒ๋“ค์—ˆ์Œ : flowA์˜ ์ •๋ณด๋Š” ์ด์ œ flowA ์ž์ฒด์— ์บก์Šํ™”๋˜์–ด ์žˆ์Œ

val filtered = flowA // ctxA๋Š” flowA์— ์บก์ˆ ํ™”๋˜์–ด ์žˆ์Œ
   .filter { it == 3 } // filter๋Š” ์•„์ง์€ context๊ฐ€ ์—†์ด ํ“จ์–ดํ•œ ์—ฐ์‚ฐ์ž์ž„

withContext(Dispatchers.Main) {
    // ๋ชจ๋“  ์บก์Šํ™”๋˜์ง€ ์•Š์€ ์—ฐ์‚ฐ์ž๋“ค์€ Main์—์„œ ์‹คํ–‰๋  ๊ฑฐ์ž„ : filter and single
    val result = filtered.single()
    myUi.text = result
}  

flow๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ๋Š” ๊ฐ™์€ ์ฝ”๋ฃจํ‹ด์—์„œ๋งŒ emit์„ ํ•ด์•ผ ํ•œ๋‹ค. ์ด๋Ÿฌํ•œ ์ œ์•ฝ์‚ฌํ•ญ์€ ๋””ํดํŠธ flow ๋นŒ๋”์— ์˜ํ•ด ์ ์šฉ๋œ๋‹ค. flow ๋นŒ๋”๋Š” flow implementation์—์„œ ์–ด๋– ํ•œ ์ฝ”๋ฃจํ‹ด๋„ ์‹œ์ž‘์‹œํ‚ค์ง€ ์•Š์„ ๋•Œ ์“ธ ์ˆ˜ ์žˆ๋‹ค. flow์˜ implemetation์€ ๋Œ€๋ถ€๋ถ„์˜ ๊ฐœ๋ฐœํ•  ๋•Œ ๊ฐœ๋ฐœ์ž๋“ค์ด ์ €์ง€๋ฅด๋Š” ์‹ค์ˆ˜๋“ค์„ ์˜ˆ๋ฐฉํ•ด์ค€๋‹ค.

val myFlow = flow {
   // GlobalScope.launch { // is prohibited
   // launch(Dispatchers.IO) { // is prohibited
   // withContext(CoroutineName("myFlow")) // is prohibited
   emit(1) // OK
   coroutineScope {
       emit(2) // OK -- still the same coroutine
   }
}

๋งŒ์•ฝ์— flow์˜ collection์ด๋ž‘ emission์ด ์—ฌ๋Ÿฌ ์ฝ”๋ฃจํ‹ด์œผ๋กœ ๋ถ„๋ฆฌ๋˜์–ด์•ผ ํ•œ๋‹ค๋ฉด, channelFlow๋ฅผ ์“ฐ๋ฉด ๋œ๋‹ค. channelFlow๋Š” ๋ชจ๋“  ์ปจํ…์ŠคํŠธ ๋ณด์กด ์ž‘์—…๋“ค์„ ์บก์Šํ™”ํ•ด์ค€๋‹ค. ๊ทธ๋ฆฌ๊ณ  channelFlow์„ ๊ตฌ์ฒด์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ• ์ง€์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ, ๋„๋ฉ”์ธ์— ํ•œ์ •๋œ ๋ฌธ์ œ์— ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค€๋‹ค. ์ด๋Ÿฌํ•œ channelFlow ์•ˆ์—์„œ๋Š” coroutine builder๋“ค์„ ์กฐํ•ฉํ•ด์„œ ์“ธ ์ˆ˜ ์žˆ๋‹ค.

โ‰๏ธ ์„ฑ๋Šฅ ์ค‘์š” + (๋™์‹œ emit & ์ปจํ…์ŠคํŠธ jump)์ด ์—†๋‹ค๋ฉด?

flow ๋นŒ๋” ๋Œ€์‹ ์— coroutineScope ๋˜๋Š” supervisorScope๋ฅผ ์“ธ ์ˆ˜ ์žˆ๋‹ค.

  • ๋ฒ”์œ„๊ฐ€ ์ง€์ •๋œ primitive๋Š” CoroutineScope๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜์–ด์•ผ ํ•จ
  • withContext(ctx)์ด๋“ , launch(ctx)์™€ ๊ฐ™์ด ๋นŒ๋”์˜ ์ธ์ž๋“  ๊ฐ„์— emission์˜ ์ปจํ…์ŠคํŠธ๋ฅผ ๋ฐ”๊พธ๋Š”๊ฑด ์•ˆ๋จ
  • ๋ณ„๊ฐœ์˜ ์ปจํ…์ŠคํŠธ์—์„œ ๋‹ค๋ฅธ flow๋ฅผ collectํ•  ์ˆ˜๋Š” ์žˆ์Œ. ๊ทผ๋ฐ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด flowOn ์—ฐ์‚ฐ์ž๋ฅผ ์ ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๋˜‘๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค๊ธฐ๋Š” ํ•˜๋‚˜, flowOn์ด ๋” ํšจ์œจ์ ์ž„

(2) Exception transparency

emit์ด๋‚˜ emitAll์ด ์˜ˆ์™ธ๋ฅผ ๋˜์งˆ ๋•Œ, Flow implementation์—์„œ๋Š” ์ƒˆ๋กœ์šด ๊ฐ’์„ emitํ•˜๋Š” ๊ฒƒ์„ ๋ฉˆ์ถ”๊ณ  exception์œผ๋กœ finishํ•ด์ค˜์•ผ ํ•œ๋‹ค. downstream์ด ์‹คํŒจํ•œ ํ›„์— ๊ฐ’๋“ค์„ emitํ•ด์•ผ ํ•œ๋‹ค๋ฉด, catch ์—ฐ์‚ฐ์ž๋ฅผ ์“ฐ๋ฉด ๋œ๋‹ค. catch ์—ฐ์‚ฐ์ž๋Š” upstream exception๋งŒ ์žก์•„์ฃผ๊ณ , ๋ชจ๋“  downstream exception์„ ๊ทธ๋ƒฅ ํ†ต๊ณผ์‹œ์ผœ๋ฒ„๋ฆฐ๋‹ค. ์ด์™€ ์œ ์‚ฌํ•˜๊ฒŒ collect ๊ฐ™์€ terminal ์—ฐ์‚ฐ์ž๋“ค์€ ์ฝ”๋“œ๋‚˜ upstream flow์—์„œ ๋‚˜ํƒ€๋‚œ unhandled exception์„ throwํ•œ๋‹ค.

flow { emitData() }
    .map { computeOne(it) }
    .catch { ... } // catches exceptions in emitData and computeOne
    .map { computeTwo(it) }
    .collect { process(it) } // throws exceptions from process and computeTwo

๊ฐ™์€ ์ถ”๋ก ์ด finally ๋ธ”๋ก์„ ๋Œ€์ฒดํ•ด์ฃผ๋Š” onCompletion ์—ฐ์‚ฐ์ž์—๋„ ์ ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค. ๋ชจ๋“  exception์„ ์ฒ˜๋ฆฌํ•˜๋Š” Flow์˜ ์—ฐ์‚ฐ์ž๋“ค์€ exception suppression ์›์น™์„ ๋”ฐ๋ฅธ๋‹ค. ๋งŒ์•ฝ์— downstream exception์ด throw๋˜์—ˆ์„ ๋•Œ upstream flow๊ฐ€ completion ์ค‘์— exception์„ throwํ•œ๋‹ค๋ฉด, downstream exception์€ upstream exception์— ์˜ํ•ด ๋Œ€์ฒด๋˜๊ณ  suppress๋˜๋ฉฐ finally ๋ธ”๋ก์—์„œ throwํ•˜๋Š” ๊ฒƒ๊ณผ ์˜๋ฏธ์ƒ ๋™์ผํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ downstream exception์„ root cause๋กœ ์—ฌ๊ธฐ๊ณ  uptream์€ ์•„๋ฌด๊ฒƒ๋„ throwํ•˜์ง€ ์•Š์€ ๊ฒƒ์ฒ˜๋Ÿผ ์ž‘๋™ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, exception์„ ํ•ธ๋“ค๋งํ•˜๋Š” ์—ฐ์‚ฐ์ž๋“ค์˜ operation์— ์˜ํ–ฅ์„ ๋ผ์น˜์ง€๋Š” ์•Š๋Š”๋‹ค.
์ œ๋Œ€๋กœ exception transparency๋ฅผ ์ง€ํ‚ค์ง€ ์•Š์œผ๋ฉด, ์ฝ”๋“œ์— ๋Œ€ํ•œ local reasoning์„ ์ œ๋Œ€๋กœ ํ•˜์ง€ ๋ชปํ•˜๊ณ  collect {โ€ฆ} ์—์„œ์˜ exception์ด upstream flow์— ์˜ํ•ด โ€œcaughtโ€๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๋ฅผ ์ถ”๋ก ํ•˜๊ธฐ ์–ด๋ ค์›Œ์ ธ์„œ ์ด์ƒํ•˜๊ฒŒ ์ž‘๋™ํ•˜๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.
Flow๋Š” ๋Ÿฐํƒ€์ž„์— exception transparency๋ฅผ ๊ฒ€์‚ฌํ•ด์„œ ๋งŒ์•ฝ์— exception์ด ์ด์ „ ์‹œ๋„์—์„œ throw๋˜์—ˆ๋‹ค๋ฉด, ๊ฐ’์„ emitํ•˜๋ ค๊ณ  ์‹œ๋„ํ•  ๋•Œ๋งˆ๋‹ค IllegalStateException์„ throwํ•œ๋‹ค.

Reactive streams

Flow๋Š” Reactive streams์™€ ํ˜ธํ™˜๋œ๋‹ค. Flow.asPublisher๊ณผ Publisher.asFlow๋ฅผ ์จ์„œ reactive stream๊ณผ ์ƒํ˜ธ์šด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

Not stable for inheritance

Flow ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋‹ค๋ฅธ ์˜คํ”ˆ์†Œ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ƒ์†ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ธฐ์— ์ ํ•ฉํ•˜์ง€๋Š” ์•Š๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์ƒˆ๋กœ์šด ๋ฉ”์†Œ๋“œ๋“ค์ด ์ถ”ํ›„์— Flow ์ธํ„ฐํŽ˜์ด์Šค์— ์ถ”๊ฐ€๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
๊ตฌํ˜„ํ•˜๋ ค๋ฉด flow {โ€ฆ} ๋นŒ๋” ํ•จ์ˆ˜๋ฅผ ์“ฐ๊ฑฐ๋‚˜, AbstractFlow๋ฅผ extendํ•ด์•ผ ํ•œ๋‹ค. ๋‘ ๋ฐฉ๋ฒ•์€ ๋ชจ๋‘ ์ปจํ…์ŠคํŠธ ๋ณด์กด ํŠน์„ฑ์„ ์œ„๋ฐ˜ํ•˜์ง€ ์•Š์œผ๋ฉฐ ๊ฐœ๋ฐœ์ž๊ฐ€ concurrency์™€ ๊ด€๋ จ๋œ ์‹ค์ˆ˜๋ฅผ ํ•˜๊ฑฐ๋‚˜, ์ผ๊ด€์„ฑ์—†๊ฒŒ flow dispatcher๋‚˜ cancellation์„ ์“ฐ๋Š” ๊ฒƒ์„ ์˜ˆ๋ฐฉํ•ด์ค„ ๊ฒƒ์ด๋‹ค.

[์ฐธ๊ณ  ์‚ฌ์ดํŠธ]
Kotlin ๊ณต์‹ ๋ฌธ์„œ - Flow

Join Newsletter
Get the latest news right in your inbox. We never spam!
Ella
Written by Ella Follow
Android Developer, love to explore new ideas and write on my morning coffee!