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

Kotlin Coroutines 101

Kotlin Coroutines 101

As the title suggests this is a complete 101 guide to Kotlin coroutine. The talk will cover the basics of coroutines and how to get started with them. We will look at how to incorporate coroutines in your applications. We will also look at why coroutines are better for asynchronous programming and what other solutions exist today for achieving async and their pain-points.

Sagar Viradiya

August 24, 2019
Tweet

More Decks by Sagar Viradiya

Other Decks in Programming

Transcript

  1. Coroutines 101
    A Newbie guide to coroutines
    @viradiya_sagar

    View Slide

  2. What is Coroutine?
    Coroutines are lightweight threads which allows to write async code in sequential
    manner.

    View Slide

  3. View Slide

  4. What is Coroutine?
    1. Coroutines are like lightweight threads.
    2. Allows to write async code in sequential manner.

    View Slide

  5. Why Async?

    View Slide

  6. Main thread (UI thread)
    View Inflation
    Measure + Layout
    Draw
    Many more things...
    Your code

    View Slide

  7. Refresh Rate
    60 HZ ~16 ms

    View Slide

  8. Refresh Rate
    90 HZ ~12 ms

    View Slide

  9. Refresh Rate
    120 HZ ~8.3 ms

    View Slide

  10. How Async ?

    View Slide

  11. Callback
    Display list of tweets
    1. Get token
    2. Get list of tweets

    View Slide

  12. fun getToken(cb: (Token) -> Unit) {
    //Makes a request for token, invokes callback when done
    //returns immediately
    }
    fun getTweets(token: Token, cb: (List) -> Unit) {
    //Makes a request for tweets, invokes callback when done
    //returns immediately
    }

    View Slide

  13. fun displayTweets() {
    getToken { token ->
    getTweets(token) { tweets ->
    //Consume list of tweets
    }
    }
    }

    View Slide

  14. Callback
    Display list of tweets
    1. Get token
    2. Get list of tweets
    3. Process tweets

    View Slide

  15. fun getToken(cb: (Token) -> Unit) {
    //Makes a request for token, invokes callback when done
    //returns immediately
    }
    fun getTweets(token: Token, cb: (List) -> Unit) {
    //Makes a request for tweets, invokes callback when done
    //returns immediately
    }
    fun processTweets(tweets: List, cb: (List) -> Unit) {
    //Process list of tweet, invoke callback when done
    //returns immediately
    }

    View Slide

  16. fun displayTweets() {
    getToken { token ->
    getTweets(token) { tweets ->
    processTweets(tweets) { tweets ->
    //Consume tweets
    }
    }
    }
    }

    View Slide

  17. View Slide

  18. Reactive
    programming
    fun displayTweets() {
    val disposable = getToken()
    .flatMap(token: Token) {
    return getTweets(token)
    }
    .map(tweets: List) {
    //Perform processing
    }
    .subscribe(
    tweets -> {
    //Consume tweets
    },
    throwable -> {
    //Handle error
    }
    )
    }

    View Slide

  19. Coroutines
    way to Async
    suspend fun getToken(): Token {
    //Get token from n/w call
    return token
    }
    suspend fun getTweet(token: Token): List {
    //Get tweets using token from n/w call
    return tweets
    }
    suspend fun processTweet(tweets: List): List {
    //Perform processing on list
    return updatedTweets
    }

    View Slide

  20. suspend fun displayTweets() {
    val token = getToken()
    val tweets = getTweets(token)
    val updatedTweets = processTweets(tweets)
    //Consume updatedTweets
    }

    View Slide

  21. Coroutines
    1. Coroutines are like light weight threads.
    2. It allows you to write async code in sequential manner.

    View Slide

  22. fun main() = runBlocking {
    repeat(100_000) { // launch a lot of coroutines
    launch {
    delay(1000L)
    print(".")
    }
    }
    }

    View Slide

  23. Suspending function
    Non blocking function
    Suspends the execution of coroutine and resumes execution.
    Concept of continuation.

    View Slide

  24. View Slide

  25. suspend fun displayTweets() {
    val token = getToken()
    val tweets = getTweets(token)
    val updatedTweets = processTweets(tweets)
    //Consume updatedTweets
    }

    View Slide

  26. ync suspend fun getToken(): Token {
    //Get token from n/w call
    return token
    }
    suspend fun getTweet(token: Token): List {
    //Get tweets using token from n/w call
    return tweets
    }
    suspend fun processTweet(tweets: List): List {
    //Perform processing on list
    return updatedTweets
    }

    View Slide

  27. Concept of
    continuation
    void getToken(Continuation continuation) {
    //Get token from n/w call
    continuation.resume(token)
    }
    void getTweets(Token token,
    Continuation> continuation) {
    //Get tweets using token from n/w call
    continuation.resume(tweets)
    }
    void processTweets(List tweets,
    Continuation> continuation) {
    //Perform processing on list
    continuation.resume(updatedTweets)
    }

    View Slide

  28. interface Continuation {
    val context: CoroutineContext
    fun resume(value: T)
    fun resumeWithException(exception: Throwable)
    }

    View Slide

  29. Coroutine scope
    Lifecycle of coroutine
    Scope encapsulate coroutine context.

    View Slide

  30. public interface CoroutineScope {
    public val coroutineContext: CoroutineContext
    }

    View Slide

  31. GlobalScope.launch { // launch new coroutine in background and continue
    delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
    println("Hello World!") // print after delay
    }

    View Slide

  32. class MainActivity : AppCompatActivity(), CoroutineScope {
    private lateinit var job: Job
    override val coroutineContext: CoroutineContext
    get() = Dispatchers.Main + job
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    job = Job()
    runTask()
    ...
    }
    fun runTask() {
    launch {
    //Perform task here
    }
    }
    override fun onDestroy() {
    job.cancel()
    super.onDestroy()
    }
    }

    View Slide

  33. Coroutine context
    Job
    Dispatcher
    Coroutine name
    Exception handler

    View Slide

  34. Coroutine builders
    Launch
    Async
    runBlocking

    View Slide

  35. Launch
    fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
    ): Job

    View Slide

  36. Launch
    val job = GlobalScope.launch { // launch new coroutine in background and continue
    delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
    println("Hello World!") // print after delay
    }
    .
    .
    .
    job.cancel()

    View Slide

  37. Async - Await
    Use for concurrent tasks which are independent of each other.
    Await - Suspending function to get result from async.

    View Slide

  38. Async
    fun CoroutineScope.async(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> T
    ): Deferred

    View Slide

  39. suspend fun displayTweets() {
    val token = getToken()
    val tweets1 : Deferred> = async(Dispatchers.Default) { getTweets(token, "#IPL") }
    val tweets2 : Deferred> = async(Dispatchers.Default) { getTweets(token, "#EPL") }
    val updatedTweets = processTweets(tweets1.await() + tweets2.await())
    //Consume updatedTweets
    }

    View Slide

  40. runBlocking
    Blocks the current thread until coroutine completes.
    Useful in unit testing.

    View Slide

  41. fun main() = runBlocking { // start main coroutine
    GlobalScope.launch { // launch new coroutine in background and continue
    delay(1000L)
    println("World!")
    }
    println("Hello,") // main coroutine continues here immediately
    }

    View Slide

  42. Coroutine dispatcher
    Dispatcher.Default
    Dispatcher.IO
    Dispatcher.Unconfined
    Dispatcher.Main (Need separate dependency for each UI framework)

    View Slide

  43. coroutineScope
    withContext
    Scoping functions

    View Slide

  44. coroutineScope
    Designed for parallel
    decomposition of work.
    suspend fun doThingsParallelly() = coroutineScope {
    val result1 = async { //Task1 }
    val result2 = async { //Task2 }
    result1.await() + result2.await()
    }

    View Slide

  45. withContext
    Mostly for thread switching.
    launch { mainScope ->
    .
    .
    .
    val result = withContext(Dispatcher.Default) {
    //Perform heavy task here
    }
    .
    .
    .
    }

    View Slide

  46. Summary
    1. Need for async programming.
    2. Ways to achieve async programming.
    3. Suspending functions.
    4. Coroutines scope.
    5. Coroutines context.
    6. Coroutines builders.
    7. Coroutines dispatchers.
    8. Scoping functions.

    View Slide

  47. Resources
    Kotlin official doc - https://kotlinlang.org/docs/reference/coroutines/basics.html
    Blog post - https://antonioleiva.com/coroutines/
    Videos
    ● KotlinConf 2018 - Exploring Coroutines in Kotlin by Venkat Subramariam
    https://youtu.be/jT2gHPQ4Z1Q
    ● KotlinConf 2017 - Introduction to Coroutines by Roman Elizarov
    https://youtu.be/_hfBv0a09Jc
    ● KotlinConf 2018 - Android Suspenders by Chris Banes
    https://youtu.be/P7ov_r1JZ1g

    View Slide

  48. Thank you !

    View Slide

  49. Questions ?

    View Slide