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

Advanced Kotlin Coroutines: Working with Channels, Contexts and Flows

Advanced Kotlin Coroutines: Working with Channels, Contexts and Flows

Kotlin/Everywhere is a worldwide series of community-driven events, sponsored by Google and JetBrains, focussing on the potential of Kotlin on all platforms.

This talk focuses on Kotlin Coroutines, its underlying concepts such as structured concurrency, suspending vs. blocking and advanced concepts e.g. contexts, channels and flows.

André Diermann

November 08, 2019
Tweet

More Decks by André Diermann

Other Decks in Programming

Transcript

  1. - Coroutine basics - Contexts, Dispatchers and Scopes - Channels

    and Flows - Conclusion Agenda 08.11.2019 2
  2. Kotlin Coroutines 08.11.19 4 „Coroutines are like light-weight threads.“ Roman

    Elizarov „The main purpose of coroutines is to simplify asynchronous programming.“ Svetlana Isakova
  3. Terminology 08.11.19 5 Coroutines Suspending Function launch Scope Context Dispatcher

    runBlocking async / await Deferred Job Channel Flow Sequential vs. Concurrent Structured Concurrency Suspending vs. Blocking
  4. Example – Terminology 08.11.19 6 Suspending Function Coroutine Builder Context

    Scope Blocking Statement Suspension Point Dispatcher Coroutine Job
  5. Example – Suspending vs. Blocking 08.11.19 7 What is the

    output of this function? Call takes 500ms, and returns [„data_1“] Recommendation: https://medium.com/@elizarov/blocking-threads-suspending-coroutines-d33e11bf4761 Suspending Functions do not block the caller thread
  6. Example – Waiting for a Job 08.11.19 8 A coroutine

    itself is represented by a Job. It is responsible for coroutine’s lifecycle, cancellation, and parent-child relations. „ “ Recommendation: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/
  7. A coroutine dispatcher determines what thread or threads the corresponding

    coroutine uses for its execution. The coroutine dispatcher can confine coroutine execution to a specific thread, dispatch it to a thread pool, or let it run unconfined. Dispatcher 08.11.19 14 „ “ Dispatchers.Default It uses a common pool of shared background threads. This is an appropriate choice for compute-intensive coroutines that consume CPU resources. Dispatchers.IO It uses a shared pool of on-demand created threads and is designed for off- loading of IO-intensive blocking operations (like file I/O + blocking socket I/O). Dispatchers.Main A coroutine dispatcher that is confined to the Main thread operating with UI objects. Requires certain UI libraries in the classpath. Dispatchers.Unconfined It starts coroutine execution in the current call-frame until the first suspension. On first suspension the coroutine builder function returns. newSingleThreadContext It creates a dedicated thread for the coroutine to run. Note: Marked as „obsolete API“
  8. Context 08.11.19 16 Recommendation: https://medium.com/@elizarov/coroutine-context-and-scope-c8b255d59055 https://proandroiddev.com/demystifying-coroutinecontext-1ce5b68407ad Coroutines always execute in

    some context. The coroutine context is a set of various elements. The main elements are the job of the coroutine and its dispatcher. „ “ Jumping between threads Combining context elements Debugging with -Dkotlinx.coroutines.debug More elements: CoroutineExceptionHandler ContinuationInterceptor
  9. Discussion – Where to define the dispatcher? 08.11.19 17 When

    consuming a suspension function? When defining a suspension function? Do you have a preferred approach?
  10. Scope 08.11.19 18 Each coroutine is launched in a particular

    scope. A scope is an abstract encapsulation to manage multiple coroutines, contexts, childrens and jobs within a common lifecycle. „ “ Timeline User navigates to a screen. Screen loads data in the background. User navigates to another screen. Background tasks completes while its caller is already killed. Memory Leak Timeline User navigates to a screen. Screen loads data in the background. User navigates to another screen. Scope gracefully cancels background tasks. Unscoped Scoped
  11. Scopes on Android 08.11.19 19 Source: https://developer.android.com/topic/libraries/architecture/coroutines ViewModelScope A ViewModelScope

    is defined for each ViewModel in your app. Any coroutine launched in this scope is automatically canceled if the ViewModel is cleared. Coroutines are useful here for when you have work that needs to be done only if the ViewModel is active. For example, if you are computing some data for a layout, you should scope the work to the ViewModel so that if the ViewModel is cleared, the work is canceled automatically to avoid consuming resources. LifecycleScope A LifecycleScope is defined for each Lifecycle object. Any coroutine launched in this scope is canceled when the Lifecycle is destroyed. You can access the CoroutineScope of the Lifecycle either via lifecycle.coroutineScope or lifecycleOwner.lifecycleScope properties.
  12. Example – ViewModelScope 08.11.19 20 Recommendation: https://medium.com/androiddevelopers/easy-coroutines-in-android-viewmodelscope-25bffb605471 Suspension function is

    automatically cancelled when ViewModel is destroyed ContextProvider is injected to increase testability ViewModelScope manages two different contexts working on different Threads.
  13. Asynchronous Flow 08.11.19 22 Recommendation: https://medium.com/@elizarov/simple-design-of-kotlin-flow-4725e7398c4c A Flow represents a

    cold asynchronous data stream that sequentially emits values and completes normally or with an exception. The code inside a flow builder does not run until the flow is collected. „ “ Call takes 300ms and returns [„fetch_1“, „fetch_2“, „fetch_3“] Each emit takes 150ms: [„stream_1“, „stream_2“, „stream_3“] What is the output of this function?
  14. Flow builders and operators 08.11.19 23 Builders flowOf(…) Creates a

    flow from a fixed set of values. asFlow() Extension functions on various types to convert them into flows. flow{…} A builder function to construct arbitrary flows from sequential calls to emit function. channelFlow{…} A builder function to construct arbitrary flows from potentially concurrent calls to the send function. Source: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/ Operators Intermediate operators Such as map, filter, take, zip, etc are functions that are applied to the upstream flow or flows and return a downstream flow where further operators can be applied to. Intermediate operations do not execute any code in the flow and are not suspending functions themselves. They only set up a chain of operations for future execution and quickly return. This is known as a cold flow property. Terminal operators Are either suspending functions such as collect, single, reduce, toList, etc. or launchIn operator that starts collection of the flow in the given scope. They are applied to the upstream flow and trigger execution of all operations. Execution of the flow is also called collecting the flow and is always performed in a suspending manner without actual blocking. Terminal operators complete normally or exceptionally depending on successful or failed execution of all the flow operations in the upstream.
  15. Flow – A real world example 08.11.19 24 Source: LiveData

    with Coroutines and Flow (Android Dev Summit ‚19) – https://youtu.be/B8ppnjGPAGE
  16. Flow – A real world example 08.11.19 25 Source: LiveData

    with Coroutines and Flow (Android Dev Summit ‚19) – https://youtu.be/B8ppnjGPAGE
  17. Flow – A real world example 08.11.19 26 Source: LiveData

    with Coroutines and Flow (Android Dev Summit ‚19) – https://youtu.be/B8ppnjGPAGE
  18. Flow – A real world example 08.11.19 27 Source: LiveData

    with Coroutines and Flow (Android Dev Summit ‚19) – https://youtu.be/B8ppnjGPAGE Since 2019-10-09: Stable Coroutine Flow support in Room version 2.2.0. https://developer.android.com/jetpack/androidx/releases/room#2.2.0
  19. Flow – A real world example 08.11.19 28 Source: LiveData

    with Coroutines and Flow (Android Dev Summit ‚19) – https://youtu.be/B8ppnjGPAGE Using the full power of Coroutine‘s Flow. Bridging between Flow and LiveData with simple extension function.
  20. Channel 08.11.19 29 Channel is a non-blocking primitive for communication

    between sender and receiver. Conceptually, a channel is similar to BlockingQueue, but it has suspending operations instead of blocking ones and it can be closed. „ “ Channels provide a way to transfer a stream of values between coroutines. Source: https://kotlinlang.org/docs/reference/coroutines/channels.html
  21. - Several concepts and techniques are summarized under the term

    „Coroutines“ - Coroutines allow asynchronous programming with minimal boilerplate - Scopes and structured concurrency help you to avoid memory leaks - There are different dispatchers for different kind of jobs and you can jump between them - Flows are a Kotlin native approach for Reactive Streams - Channels and Deferred allow to exchange data among two or more coroutines Conclusion 08.11.19 34
  22. - KotlinConf 2017 - Introduction to Coroutines by Roman Elizarov

    // https://youtu.be/_hfBv0a09Jc - KotlinConf 2017 - Deep Dive into Coroutines on JVM by Roman Elizarov // https://youtu.be/YrrUCSi72E8 - KotlinConf 2018 - Kotlin Coroutines in Practice by Roman Elizarov // https://youtu.be/a3agLJQ6vt8 - KotlinConf 2018 - Exploring Coroutines in Kotlin by Venkat Subramaniam // https://youtu.be/jT2gHPQ4Z1Q - DevFest 2018 - Kotlin Coroutines by Svetlana Isakova // https://youtu.be/BXwuYykIxbk - Google IO 2019 - Understand Kotlin Coroutines on Android by Yigit Boyar et. al. // https://youtu.be/BOHK_w09pVA Further Readings 08.11.19 35
  23. In order of appearance: - Photo by Tom Winckels on

    Unsplash - Photo by niko photos on Unsplash - Photo by Robert Lukeman on Unsplash - Photo by Aniket Deole on Unsplash - Photo by Cris Saur on Unsplash Images & Attribution 08.11.19 37