Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kotlin Coroutines and Flow.

Kotlin Coroutines and Flow.

코틀린 코루틴과 플로우에 대한 설명을 포함

TaeHwan

July 26, 2021
Tweet

More Decks by TaeHwan

Other Decks in Programming

Transcript

  1. Coroutines
    Taehwan

    View Slide

  2. য়ט ঌইࠅ ղਊ
    • ࢎਊߨ਷ ݒ਋ рױ೤פ׮.

    • ࢎਊߨࠁױ ղࠗ ௏٘ܳ ా೧ ௏ܖ౯ਸ ઑӘ ؊ ࢓ಝࠇפ׮.

    • ҃೷࢚ ԙ ঌইفݶ જਸ ղਊਸ ୶о೤פ׮.

    View Slide

  3. Subroutine

    View Slide

  4. Subroutine
    • In computer programming, a subroutine is a sequence of program
    instructions that performs a speci
    fi
    c task, packaged as a unit

    • In di
    ff
    erent programming languages, a subroutine may be called a
    procedure, a function, a routine, a method, or a subprogram. The generic
    term callable unit is sometimes used.

    View Slide

  5. Subroutine
    fun runTest() fun MutableList.sum()
    mutableList().sum()
    return sumValue
    +1


    +2


    .


    .


    .

    View Slide

  6. Coroutines

    View Slide

  7. Coroutines
    • According to Donald Knuth, Melvin Conway coined the term coroutine in
    1958 when he applied it to construction of an assembly program.

    • The
    fi
    rst published explanation of the coroutine appeared later, in 1963

    View Slide

  8. Coroutines
    • Coroutines are computer-program components that
    generalize subroutines for non-preemptive multitasking, by allowing
    multiple entry points for suspending and resuming execution at certain
    locations.

    • Coroutines are well-suited for implementing familiar program components
    such as cooperative tasks, exceptions, event loops, iterators, in
    fi
    nite
    lists and pipes.
    ୹୊ : https://en.wikipedia.org/wiki/Coroutine

    View Slide

  9. Coroutines
    Run routine fun Int.sum()
    Loop
    fun Int.sum()
    Loop
    fun Int.sum()
    Loop
    fun Int.sum()
    Loop
    10.sum()
    .

    .

    .
    Entry point ৈ۞ ѐ ೲਊ

    View Slide

  10. Kotlin Coroutines

    View Slide

  11. Kotlin Coroutines
    • Asynchronous or non-blocking programming is an important part of the
    development landscape. When creating server-side, desktop, or mobile
    applications, it's important to provide an experience that is not only
    fl
    uid
    from the user's perspective, but also scalable when needed.

    • Kotlin solves this problem in a
    fl
    exible way by providing coroutine support
    at the language level and delegating most of the functionality to libraries.

    • In addition to opening the doors to asynchronous programming,
    coroutines also provide a wealth of other possibilities, such as
    concurrency and actors.

    View Slide

  12. Kotlin Coroutines
    Default :
    dependencies {
    implementation(“org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1”)
    }
    Android :
    dependencies {
    implementation(“org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1”)
    }
    repository {
    mavenCentral()
    }
    Androidীࢲח dependencies ࢎਊ द coroutines-androidܳ ӝࠄਵ۽ ഝਊೞݶ ؾפ׮. উ٘۽੉٘৬ ޖҙೠ ݽٕ۽ ഝਊೡ Ѫ੉ۄݶ
    coroutines-coreܳ ഝਊೞݶ ؾפ׮.

    View Slide

  13. Kotlin Coroutines
    class MainViewModel : ViewModel() {
    init {
    viewModelScope.launch(Dispatchers.IO) {
    Log.i("TEMP", getMessage())
    }
    Log.i("TEMP", "Hello")
    }
    private suspend fun getMessage(): String {
    delay(1_000L)
    return "World!"
    }
    }
    AAC-ViewModelਸ ഝਊೡ ҃਋੄ ௏ܖ౯ ࢎਊߨ੉ݴ, launch ز੘द ࢎਊೡ ӝࠄ झா઴۞ܳ Dispatchersਸ IO۽ ߸҃೤פ׮.

    View Slide

  14. Kotlin Coroutines
    public val ViewModel.viewModelScope: CoroutineScope
    get() {
    val scope: CoroutineScope? = this.getTag(JOB_KEY)
    if (scope != null) {
    return scope
    }
    return setTagIfAbsent(
    JOB_KEY,
    CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
    )
    }
    internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
    override val coroutineContext: CoroutineContext = context
    override fun close() {
    coroutineContext.cancel()
    }
    }
    AAC-ViewModel੄ ഛ੢ ೣࣻ۽ viewModelScope੄ ղࠗ ௏٘ੑפ׮. CoroutineScopeਸ ղࠗ੸ਵ۽ ࢎਊೞҊ, cancel दఃӝ ਤೠ
    Closable ࢚ࣘ ௿ېझо ҳഅغয ੓णפ׮. AAC-ViewModel destroy द ೣԋ ઙܐೞب۾ ٜ݅যઉ੓Ҋ, SupervisorJob()җ Mainਸ ӝ
    ࠄਵ۽ ࢎਊ೤פ׮.

    View Slide

  15. ੉ ௏٘۽ ঌইࠅ ղਊ
    • CoroutinesScope(GlobalScope਷ ૑ন)

    • Coroutines ز੘ೞӝ ਤೠ launch

    • CoroutineContext(SupervisorJob, Dispatchers)

    • suspend function

    • Coroutine ઙܐ ୊ܻ

    View Slide

  16. CoroutinesScope

    View Slide

  17. CoroutinesScope
    public interface CoroutineScope {
    /**
    * The context of this scope.
    * Context is encapsulated by the scope and used for implementation of coroutine
    builders that are extensions on the scope.
    * Accessing this property in general code is not recommended for any purposes except
    accessing the [Job] instance for advanced usages.
    *
    * By convention, should contain an instance of a [job][Job] to enforce structured
    concurrency.
    */
    public val coroutineContext: CoroutineContext
    }
    CoroutineScope੄ ୭࢚ਤ interface ੿੄ੑפ׮.

    View Slide

  18. CoroutinesScope ৉ೡ
    • Coroutine ੿੄ܳ ਤೠ Scope ઁҕ

    • CoroutineContext ഋకܳ ૑੿

    • Default, IO, Android Main

    • launch, async ١ਸ ా೧ coroutine प೯ ഋకܳ ੿੄

    View Slide

  19. Coroutines ز੘ೞӝ ਤೠ launch

    View Slide

  20. Coroutines ز੘ೞӝ ਤೠ launch
    class MainViewModel : ViewModel() {
    init {
    viewModelScope.launch(Dispatchers.IO) {
    // 생략
    }
    }
    }

    View Slide

  21. Coroutines ز੘ೞӝ ਤೠ launch
    public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
    ): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
    LazyStandaloneCoroutine(newContext, block) else
    StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
    }
    launch੄ start ӝࠄ ز੘਷ CoroutineStart.DEFAULT۽ ૊द प೯ਸ ӝࠄਵ۽ ೠ׮.


    LAZY۽ ߸҃ द return ೠ Jobਸ ੉ਊ೧ lazy start() ೡ ࣻ ੓׮.


    contextח ߹ب۽ ૑੿ೞ૑ ঋਵݶ EmptyCoroutineContext੉ݴ, newCoroutineContext۽ parent CoroutineScope੄ context৬ ೤
    ࢿೠ׮.
    newCoroutineContext(context)
    start: CoroutineStart = CoroutineStart.DEFAULT,
    context: CoroutineContext = EmptyCoroutineContext,
    : Job
    CoroutineScope.launch

    View Slide

  22. Coroutines ز੘ೞӝ ਤೠ launch
    • CoroutineScopeਸ प೯ೞӝ ਤೠ launch, async/await ١੉ ੓׮

    • launch ࢎਊद ೦࢚ ࢚ਤ Context + ࢜۽਍ Conextܳ ೤୛ newContext() ࢤࢿ

    • launch੄ ӝࠄ ز੘਷ ૊द प೯੉׮.

    • launch੄ return ч਷ Jobਸ ߈ജ, प೯ Ѿҗܳ ঳Ҋ र׮ݶ async/await ഝਊ

    View Slide

  23. CoroutineStart
    • DEFAULT : ૊द प೯, ݽٚ ௏ܖ౯਷ DEFAULTо ӝࠄ

    • LAZY : וܻӝ प೯, job ё୓ܳ ੉ਊ start()ܳ ഐ୹೧઻ঠ ೠ׮

    View Slide

  24. Coroutines ز੘ೞӝ ਤೠ launch
    class MainViewModel : ViewModel() {
    init {
    val job = viewModelScope.launch(start = CoroutineStart.LAZY, context = Dispatchers.IO) {
    // 생략
    }
    job.start()
    }
    }
    launchо lazy۽ ز੘ೞӝ ਤ೧ࢲח startী ২࣌ਸ ୶оೞҊ, launch ز੘द ߹ب੄ contextо ೙ਃೞ׮ݶ Dispatchers.IOܳ ૑੿೤פ׮.


    Lazy दীח jobীࢲ start()ܳ ഐ୹೤פ׮.

    View Slide

  25. CoroutineContext

    SupervisorJob, Dispatchers, Etc

    View Slide

  26. CoroutineContext
    • CoroutineContextח ௏ܖ౯੄ ز੘, ҙܻܳ ୊ܻೞӝ ਤೠ Contextܳ ઁҕ

    View Slide

  27. CoroutineContext
    CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
    Job Dispatchers
    CoroutinesContextח + ো࢑ਵ۽ ੉যࠢ੉ӝ оמ

    View Slide

  28. Dispatchers
    • Default : JVM झۨ٘ shared pool ӝળਵ۽ ز੘, CPU ௏য ࣻ৬ زੌೞ૑݅ ୭
    ࣗ 2ѐܳ ࢎਊ ( RxJava : Schedulers.computation() )

    • IO : Thread ҕਬ ಽ۽ ز੘ ( RxJava : Schedulers.io() )

    • Main : Android UI ୊ܻ ( RxJava : Android.main )

    • Uncon
    fi
    ned : ౠ੿ झۨ٘ী ઁೠغ૑ ঋח ௏ܖ౯

    View Slide

  29. Job
    • Job() : ೞա੄ ੘স੉ۄب લਵݶ ੹୓ ઙܐ

    • SupervisorJob() : ೞա੄ ੘স੉ ઙܐغযب ׮ܲ ੘স(child/parent)ী ৔ೱਸ ޷
    ஖૑ ঋ਺

    View Slide

  30. Job

    View Slide

  31. Job
    • ӝࠄ ؀ӝ

    • join() : scope ز੘੉ ՘զ ٸө૑ ؀ӝೞݴ, CoroutinScope উীࢲ ഐ୹ оמ

    • प೯

    • start() : Scope ࢚కܳ ഛੋೞݴ, ই૒ द੘ೞ૑ ঋওਸ ҃਋ start

    • ஂࣗ

    • cancelAndJoin() : ઙܐ റ ઙܐ ؀ӝ

    • cancel() : ੘স ઺ੋ ز੘ਸ ઙܐ ਬب

    • cancelChildren() : Children ੟ী ؀ೠ ઙܐ ਬب

    View Slide

  32. CoroutineName
    • ز੘ ઺ੋ Coroutine ੉ܴਸ ૑੿ೡ ࣻ ੓׮
    .launch(CoroutineName("Name"))

    View Slide

  33. CoroutineExceptionHandler
    • CoroutineExceptionHandler

    • CoroutinesScope੉ о૑ݶ ݽٚ ೞਤ job ٜ੄ Exceptionਸ ҙܻ

    • launchо о૑ݶ launch ੘স ߂ ೞਤ job ٜ੄ exceptionਸ ҙܻ

    • try/catch or kotlin.runCatching ࢎਊ द ૒੽ ҙܻೠ׮ח ੄޷

    View Slide

  34. CoroutineContext
    @Test
    fun test() = runBlocking {
    val coroutineException = CoroutineExceptionHandler { coroutineContext, throwable ->
    val name = coroutineContext[CoroutineName.Key]
    println("name $name + throwable ${throwable.message}")
    }
    val job = CoroutineScope(Dispatchers.IO).launch(coroutineException + CoroutineName("Name")) {
    kotlin.runCatching {
    throw Exception("runCatching")
    }.onFailure {
    println("throwable runCatching ${it.message}")
    }
    throw Exception("Out")
    }
    job.join()
    }
    coroutineExceptionਸ launchী ನೣೞҊ, CoroutineNameਸ ૑੿ೞ৓णפ׮. coroutineExceptionীࢲח CoroutineNameਵ۽ যځ
    ೠ ز੘ীࢲ য়ܨо ߊࢤೞ৓ח૑ ҳ࠙ೡ ࣻ ੓ѱ ؾפ׮.


    runCatchingਸ ࢎਊೞݶ ૒੽ য়ܨܳ ҙܻೣਸ ڷೞҊ, ݅ড ҕా੄ য়ܨܳ ୶о۽ ੟ইঠ ೠ׮ݶ throwܳ ୶о۽ ߊࢤदఃݶ ؾפ׮.

    View Slide

  35. CoroutineContext
    public interface CoroutineContext {
    public operator fun plus(context: CoroutineContext): CoroutineContext =
    if (context === EmptyCoroutineContext) this else // fast path -- avoid lambda creation
    context.fold(this) { acc, element ->
    val removed = acc.minusKey(element.key)
    if (removed === EmptyCoroutineContext) element else {
    // make sure interceptor is always last in the context (and thus is fast to get when present)
    val interceptor = removed[ContinuationInterceptor]
    if (interceptor == null) CombinedContext(removed, element) else {
    val left = removed.minusKey(ContinuationInterceptor)
    if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else
    CombinedContext(CombinedContext(left, element), interceptor)
    }
    }
    }
    }
    CoroutineContext੄ ղࠗ ௏٘ ઺ plus ࠗ࠙ੑפ׮.

    View Slide

  36. suspend function

    View Slide

  37. suspend function
    • suspend ఃਕ٘ܳ ୶оೞৈ ೣࣻ ࠙ೡ

    • suspend ೣࣻח CoroutineScope ղীࢲ ࢎਊ оמ

    View Slide

  38. suspend function ٣ஹ౵ੌ
    private final Object getMessage(Continuation $completion) {
    return "World!";
    }
    private open class StandaloneCoroutine(
    parentContext: CoroutineContext,
    active: Boolean
    ) : AbstractCoroutine(parentContext, initParentJob = true, active = active)
    public abstract class AbstractCoroutine(
    parentContext: CoroutineContext,
    initParentJob: Boolean,
    active: Boolean
    ) : JobSupport(active), Job, Continuation, CoroutineScope
    suspend function੄ ٣ஹ౵ੌ ௏٘ח getMessage() ࠗ࠙ੑפ׮. suspendܳ ನೣೡ ҃਋ Continuationਸ ౵ۄ޷ఠ۽ ߉חؘ, ੉۽ ੋ೧
    CoroutineScope ղীࢲ ௏٘о प೯غযঠ ೣਸ ੄޷೤פ׮. launch੄ ղࠗ ௏٘ܳ ୶੸೧ࠁݶ StandaloneCoroutine ղࠗ੄
    AbstractCoroutine੉ ੓Ҋ, ೠ ߣ ؊ ୶੸ೞݶ Continuationܳ ࢚ࣘ߉Ҋ ੓਺ਸ ഛੋ೧ࠅ ࣻ ੓णפ׮. * ٣ஹ౵ੌ Ѿҗח ׳ۄ૕ ࣻ ੓
    ਵפ ױࣽ ଵҊ݅ ೞࣁਃ.

    View Slide

  39. suspend function
    public interface Continuation {
    /**
    * The context of the coroutine that corresponds to this continuation.
    */
    public val context: CoroutineContext
    /**
    * Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the
    * return value of the last suspension point.
    */
    public fun resumeWith(result: Result)
    }
    suspend ٣ஹ౵ੌ द ֢୹ػ Continuation interfaceח resumeWith()ਸ ನೣ೤פ׮. Continuationীࢲ ੉੹ਵ۽ ࠂӈೡ ٸח
    resumeWith()ਸ ഐ୹೧ чਸ ֈѹ઱ݶ ੉੹੄ ࢚క۽ جইцפ׮.

    View Slide

  40. Coroutine ઙܐ ୊ܻ

    View Slide

  41. Coroutine ઙܐ ୊ܻ
    • ઙܐ ୊ܻ ߑߨ

    • Coroutine ୡӝച दী ੸ਊೠ Job ژח CoroutineContext ੉ਊೠ ઙܐ

    • пп੄ launch੄ return jobਸ ੉ਊೠ ઙܐ

    • ઙܐೞ૑ ޅೞח ࢚ട

    • for ܖ೐ীࢲח ௏ܖ౯ cancel()ਸ ೞ؊ۄب ઙܐೞ૑ ޅೠ׮ (׮਺੢ ଵઑ)

    • ܖ೐ীࢲ ௏ܖ౯ ઙܐܳ ਤೠ ߹ب੄ ୊ܻ ೙ਃ

    View Slide

  42. Coroutine ઙܐ ୊ܻ
    @Test
    fun testList() = runBlocking {
    val scope = CoroutineScope(Dispatchers.IO)
    val job = scope.launch {
    (0..1_000).forEach {
    println("data $it")
    }
    }
    scope.launch {
    delay(5)
    println("Call cancel”)
    job.cancel()
    }
    job.join()
    println("run!")
    }
    data 0
    data 1

    data 33
    Call cancel
    data 34

    data 1000
    run!
    ੉ ௏٘ח forEach {}۽ 0ࠗఠ 1_000ө૑ ܖ೐ܳ ز੘೤פ׮. 5ms റ cancel()ਸ ೮૑݅ Ѿҗח data 1000ө૑ ݽف ୹۱ റ ઙܐؾפ׮.


    ੉ ௏٘ח cancel()ਸ ೞ؊ۄب ઙܐೡ ߑߨ੉ হणפ׮.

    View Slide

  43. Coroutine ઙܐ ୊ܻ - isActive ࢚క۽ ഛੋ
    @Test
    fun testList() = runBlocking {
    val scope = CoroutineScope(Dispatchers.IO)
    val job = scope.launch {
    (0..1_000).forEach {
    if (isActive) {
    println("data $it")
    } else {
    cancel()
    }
    }
    }
    scope.launch {
    delay(5)
    println("Call cancel")
    job.cancel()
    }
    job.join()
    println("run!")
    }
    data 0
    data 1

    data 82
    Call cancel
    data 83

    data 104
    run!
    cancel੉ ز੘ೞѱ ٜ݅۰ݶ isActive ࢚కܳ ୓௼ೞҊ, ੉ܳ ੿૑ೡ ࣻ ੓ب۾ ୶о ੘স੉ ೙ਃ೤פ׮. ੉ ࢠ೒ীࢲח isActive ࢚కо ইפۄ
    ݶ cancel()ਸ ୊ܻ೤פ׮.


    forEach {} ղী cancel()ਸ ୶оೞ৓ӝী, 0~1_000ө૑о ইצ 5ms റ ઙܐ೤פ׮.

    View Slide

  44. Coroutine ઙܐ ୊ܻ - asFlow, cancellable
    @Test
    fun testList() = runBlocking {
    val scope = CoroutineScope(Dispatchers.IO)
    val job = scope.launch {
    (0..1_000).asFlow().cancellable().collect {
    println("data $it")
    }
    }
    scope.launch {
    delay(5)
    println("Call cancel")
    job.cancel()
    }
    job.join()
    println("run!")
    }
    data 0
    data 1

    data 31
    Call cancel
    data 32

    data 40
    run!
    asFlow()۽ ߸҃ೞҊ, cancellable()ਸ ୊ܻೞח ߑߨب ੓णפ׮. ੉ ࠗ࠙਷ ޙࢲী ੜ ա৬੓ਵפ ޙࢲܳ ଵҊೞࣁਃ.

    View Slide

  45. Flow

    View Slide

  46. Flow
    • Flow : ColdFlow

    • чਸ ࣽର੸ਵ۽ ൗ۰ࠁմ׮.

    • collect {}ਸ ೧ঠ ز੘

    • map,
    fi
    lter, take, zip ١җ э਷ ൒ܴ੄ ઺р ো࢑੗ܳ ઁҕ

    View Slide

  47. Flow
    @Test
    fun testFlow() = runBlocking {
    flow {
    (1..100).forEach {
    emit(it)
    }
    }
    .filter { it < 50 }
    .flowOn(Dispatchers.IO)
    .collect {
    println("data $it")
    }
    }
    fl
    ow ࢠ೒ ௏٘۽ 1ࠗఠ 100ө૑ ܖ೐ܳ ز੘ೞҊ, ࣽࢲ؀۽
    fi
    lter৬ collect{}ীࢲ ୹۱ೞח ௏٘ੑפ׮.

    View Slide

  48. Flow + combine
    @Test
    fun testFlowZip() = runBlocking {
    val flowTest = flow {
    (1..10).forEach {
    emit(it)
    }
    }
    flowOf(2).combine(flowTest) { a, b ->
    a * b
    }
    .collect {
    println("it $it")
    }
    }
    fl
    owOf۽ ೦࢚ 2৬ combineਵ۽
    fl
    owTest੄ 1ࠗఠ 10ө૑੄ чਸ ൗ۰ղ݀פ׮. ੉ٸ a * bܳ ୊ܻ೤פ׮.

    View Slide

  49. Flow

    fl
    owח
    fl
    ow {} উীࢲ emit ೧ঠ ೠ׮. (RxJava create)


    fl
    owOf()ח 1ഥ ࢿ ؘ੉ఠী ഝਊೡ ࣻ ੓׮. (RxJava just)


    fl
    owOn੄ ز੘਷ Dispatcher ੸ਊҗ زदী ׮਺
    fl
    owOn੉ য়ӝ ੹ө૑ زੌೠ झ
    ா઴۞ীࢲ ز੘ೠ׮. (RxJava subscribeOn, observeOn)

    • Collect ೣࣻח suspend function੉ݴ, CoroutineScope ղীࢲ ز੘ೠ׮.
    (RxJava subscribe)

    • Error ୊ܻח Coroutineীࢲ ࢎਊೞ؍ ߑधҗ زੌ(try/catch or
    CoroutineExceptionHandler)

    View Slide

  50. Callback ؘ੉ఠܳ Flow۽
    • ৻ࠗ CallbackEventܳ Flow۽ ੹ജೞח ߑߨ

    • callbackFlowܳ ഝਊ

    • ଵҊ ੗ܐ

    • https://thdev.tech/kotlin/2020/12/07/Coroutines-Flow-Callback/

    • https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/
    kotlinx.coroutines.
    fl
    ow/callback-
    fl
    ow.html

    View Slide

  51. Callback ؘ੉ఠܳ Flow۽
    class FlowMotionEvent {
    fun createMotionEvent(event: View): Flow = callbackFlow {
    val callback = View.OnTouchListener { _, p1 ->
    trySendBlocking(p1)
    true
    }
    event.setOnTouchListener(callback)
    awaitClose {
    event.setOnTouchListener(null)
    }
    }
    }
    awaitClose ೙ࣻ
    ௏٘ blocking হח ҃਋ী݅!

    View Slide

  52. Callback ؘ੉ఠܳ Flow۽
    public fun SendChannel.trySendBlocking(element: E): ChannelResult {
    /*
    * Sent successfully -- bail out.
    * But failure may indicate either that the channel it full or that
    * it is close. Go to slow path on failure to simplify the successful path and
    * to materialize default exception.
    */
    trySend(element).onSuccess { return ChannelResult.success(Unit) }
    return runBlocking {
    val r = runCatching { send(element) }
    if (r.isSuccess) ChannelResult.success(Unit)
    else ChannelResult.closed(r.exceptionOrNull())
    }
    }
    trySendBlocking੄ ղࠗ ௏٘ীࢲ ঌ ࣻ ੓૑݅ runBlockingਸ ഝਊೞҊ ੓णפ׮. runBlocking਷ UIী ؀ೠ lock੉ ߊࢤೡ ࣻ ੓ӝ ٸޙ
    ী trySendBlocking ௏٘੄ ࢎਊ द UI৬ ޖҙೠ Ҕীࢲ ࢎਊ੉ ೙ਃೞפ ઱੄೧ࢲ ࢎਊ೧ঠ ೤פ׮.

    View Slide

  53. SharedFlow

    View Slide

  54. SharedFlow
    • SharedFlow

    • HotFlow

    • Broadcast ߑधਵ۽ ؘ੉ఠܳ ҕਬ

    • ҕਬ ൒ܴ਷ ഝࢿ ੋझఢझо collect৬ ة݀੸ਵ۽ ઓ੤

    View Slide

  55. MutableSharedFlow
    • ৈ۞ Ҕীࢲ ਽׹ਸ ߉ইঠ ೡ ҃਋ ਬਊ

    • replay(0), extraBu
    ff
    erCapacity(0), Bu
    ff
    erOverFlow(Suspend) ࢚కܳ Ѿ੿ оמ

    • launchInਸ ݃૑݄ী ੸য઱য collect{} হ੉ ز੘ оמ

    View Slide

  56. MutableSharedFlow - ClickEvent
    private val mutableSharedFlow = MutableSharedFlow(
    extraBufferCapacity = 1,
    onBufferOverflow = BufferOverflow.DROP_OLDEST
    )
    init {
    mutableSharedFlow
    .onEach {
    Log.e("TEMP", "clickCount $it")
    }
    .flowOn(Dispatchers.Main)
    .launchIn(viewModelScope)
    }
    private var count = 0
    fun clickEvent() {
    mutableSharedFlow.tryEmit(count++)
    }
    MutableSharedFlowܳ ੉ਊೠ clickEvent ୊ܻ ࠗ࠙ੑפ׮. ௿ܼ ੉߮౟ח extraBufferܳ 1ѐܳ ୶оೞҊ, Over
    fl
    owܳ Drop oldest۽
    ୊ܻ೧ સפ׮.


    tryEmit()ਵ۽ countܳ োࣘ੸ਵ۽ ֈӝחؘ, 0, 1, 2, …ਸ ୊ܻ೤פ׮.

    View Slide

  57. MutableSharedFlow - ClickEvent
    public fun Flow.launchIn(scope: CoroutineScope): Job = scope.launch {
    collect() // tail-call
    }
    ଵҊ۽ launchIn੄ ղࠗ ௏٘ੑפ׮. ֈѹ߉਷ scopeਸ ੉ਊ೧ launchܳ ୊ܻೞҊ, collect()ਸ ഐ୹೤פ׮.

    View Slide

  58. StateFlow

    View Slide

  59. StateFlow
    • ݃૑݄ ࢚క чਸ ӝর೧ঠ ೡ ҃਋ ਬਊ

    • ୡӝച чਸ ೣԋ ֈѹঠ ೠ׮

    • زੌೠ ч਷ ޖदೠ׮

    • ੑ۱ೠ ч਷ Read-only۽ ز੘ೠ׮

    View Slide

  60. StateFlow
    private val stateFlow = MutableStateFlow(true)
    init {
    stateFlow
    .onEach {
    Log.e("TEMP", "value change $it")
    }
    .flowOn(Dispatchers.IO)
    .launchIn(viewModelScope)
    }
    fun clickEvent() {
    stateFlow.value = stateFlow.value.not()
    }
    MutableStateFlow۽ ୡӝച чਸ ೣԋ ֈѹસפ׮. StateFlowח زੌೠ ч੉ 1ഥ ੉࢚ ٜযয়ݶ ղࠗ੸ਵ۽ ޖदೞѱ ؾפ׮.


    ੉ ௏٘ীࢲ trueܳ 1ߣ ੉࢚ ֈӝݶ ୐ ߣ૩ value݅ streamਵ۽ ղ۰оҊ, ف ߣ૩ trueח ޖद೤פ׮.

    View Slide

  61. Coroutines Flow test


    Turbin

    View Slide

  62. Coroutines Flow test
    https://github.com/cashapp/turbine
    testImplementation("app.cash.turbine:turbine:0.5.2")
    Flow పझ౟ܳ ਤೠ turbineਸ ഝਊೡ ࣻ ੓णפ׮.

    View Slide

  63. Coroutines Flow test
    @OptIn(ExperimentalTime::class)
    @Test
    fun test() = runBlocking {
    flowOf("one", "two").test {
    Assert.assertEquals("one", expectItem())
    Assert.assertEquals("two", expectItem())
    expectComplete()
    }
    }
    test{}ח runBlocking {} উীࢲ ഐ୹೧ঠ ೞݴ,
    fl
    ow੄ ҃਋ ೦࢚ expectComplete()ܳ ഐ୹೧ ઱যঠ ೤פ׮.

    View Slide

  64. Coroutines SharedFlow test
    @ExperimentalTime
    @Test
    fun testSharedFlow() = runBlocking {
    val mutableSharedFlow = MutableSharedFlow(replay = 0)
    mutableSharedFlow.emit(1)
    mutableSharedFlow.test {
    Assert.assertEquals(expectItem(), 1)
    cancelAndConsumeRemainingEvents()
    }
    }
    MutableSharedFlowب turbinਸ ഝਊೡ ࣻ ੓णפ׮. SharedFlow੄ ҃਋ ݃૑݄ী cancelAndConsumeRemainingEvents()ܳ ഐ୹
    ೧ঠ ઙܐо ؾפ׮.

    View Slide

  65. RxJava > Flow or Coroutines

    View Slide

  66. RxJava vs Flow
    RxJava Flow
    ੿੄ Flowable, Observable, Single ١
    fl
    ow, SharedFlow, StateFlow
    ੹୓ झாે۞ ૑੿ subscribeOn(schedulers.io())
    fl
    owOnী ٮۄ ز੘
    झாે۞ ߸҃ observeOn(schedulers.io())
    fl
    owOn(CoroutineContext)
    झாે۞ ߧਤ observeOnীࢲ ੿੄ೠ ׮਺ࠗఠ ݽف ੸ਊ ׮਺
    fl
    owOn੄ ੿੄о য়ӝ ੹ө૑ زੌೞѱ ୊ܻ
    द੘ subscribe ੿੄о ੓যঠ ز੘
    collectী ٮۄ ز੘

    CoroutineScope ղীࢲ ز੘

    View Slide

  67. RxJava > Flow or Coroutines
    • RxJava੄ ௏٘ܳ ׼੢ ѥযղӝ য۵׮ݶ Coroutines rx2

    • https://github.com/Kotlin/kotlinx.coroutines/tree/master/reactive/
    kotlinx-coroutines-rx2

    • ӝઓ Repository੄ RxJavaܳ ߸҃ೠ׮ݶ Flowܳ ࢎਊ೧ ߸҃ оמ

    View Slide