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

Kotlin Coroutines

Kotlin Coroutines

Welcome to new reality of asynchronous programming which is finally available on Android - Kotlin Coroutines. During this presentation you will learn about coroutines, how they compare to RxJava and other solutions, what is hiding under the hood and few nice tricks that can help in real life. But be prepared - nothing is perfect, neither are coroutines.

Rostyslav Lesovyi

March 26, 2019
Tweet

More Decks by Rostyslav Lesovyi

Other Decks in Programming

Transcript

  1. Confidential Kotlin Coroutines in-depth analysis Version 1.0 Kotlin Coroutines In-depth

    analysis of Kotlin coroutines
  2. Confidential Kotlin Coroutines in-depth analysis Version 1.0 Rostyslav Lesovyi

  3. Confidential Kotlin Coroutines in-depth analysis // Welcome

  4. Confidential Kotlin Coroutines in-depth analysis DB ThreadPool Cloud ThreadPool DB

    Cloud // Welcome MemoryApi PersistentApi CloudApi Api
  5. Confidential Kotlin Coroutines in-depth analysis DB ThreadPool Cloud ThreadPool DB

    Cloud // Welcome to Async Hell MemoryApi PersistentApi CloudApi Api
  6. Confidential Kotlin Coroutines in-depth analysis // Welcome to Async Hell

    1. abstract fun put(id: String, user: User) 2. abstract fun getMemory(id: String): User? 3. abstract fun getPersistent(id: String): User? 4. abstract fun getCloud(id: String): User? 5. 6. fun get(id: String, callback: (User?) -> Unit) = 7. object : AsyncTask<String, Unit, User?>() { 8. override fun doInBackground(vararg params: String): User? { 9. val user = getMemory(params[0]) 10. ?: getPersistent(params[0]) 11. ?: getCloud(params[0]) 12. ?: return null 13. 14. put(id, user) 15. return user 16. } 17. 18. override fun onPostExecute(result: User?) = callback(result) 19. }.execute(id)
  7. Confidential Kotlin Coroutines in-depth analysis // Welcome to Async Hell

    1. fun put(id: String, user: User, callback: () -> Unit) 2. fun getMemory(id: String, callback: (User?) -> Unit) 3. fun getPersistent(id: String, callback: (User?) -> Unit) 4. fun getCloud(id: String, callback: (User?) -> Unit) 5. 6. fun getData(id: String, callback: (User?) -> Unit) { 7. getMemory(id) { memoryData -> 8. if (memoryData != null) return@getMemory callback(memoryData) 9. getPersistent(id) { dbData -> 10. if (dbData != null) { 11. put(id, dbData) { return@put callback(dbData) } 12. } 13. getCloud(id) { cloudData -> 14. if (cloudData != null) { 15. put(id, cloudData) { return@put callback(cloudData) } 16. } else { 17. return@getCloud callback(null) 18. } 19. }}}} // yeah... code doesn't fit presentation screen
  8. Confidential Kotlin Coroutines in-depth analysis // Welcome to Async Hell

    1. fun put(id: String, user: User): Completable 2. fun getMemory(id: String): Maybe<User> 3. fun getPersistent(id: String): Maybe<User> 4. fun getCloud(id: String): Maybe<User> 5. 6. fun get(id: String) = getMemory(id) 7. .switchIfEmpty(getPersistent(id)) 8. .switchIfEmpty(getCloud(id)) 9. .flatMap { user -> 10. put(id, user).andThen(Maybe.just(user)) 11. }
  9. Confidential Kotlin Coroutines in-depth analysis // Coroutines to the rescue!

    1. suspend fun put(id: String, user: User) 2. suspend fun getMemory(id: String): User? 3. suspend fun getPersistent(id: String): User? 4. suspend fun getCloud(id: String): User? 5. 6. suspend fun get(id: String): User? { 7. val user = getMemory(id) // executes immediately 8. ?: getPersistent(id) // runs on DB thread pool 9. ?: getCloud(id) // runs on network thread pool 10. ?: return null 11. 12. put(id, user) // runs on DB thread pool 13. return user 14. }
  10. Confidential Kotlin Coroutines in-depth analysis // Some Theory

  11. Confidential Kotlin Coroutines in-depth analysis Thread 1 // What is

    Singlethreading? Task 1 Task 2 Task 3 Task 4
  12. Confidential Kotlin Coroutines in-depth analysis Thread 1 // What is

    Multithreading? Thread 2 Task 1 Task 3 Task 2 Task 4
  13. Confidential Kotlin Coroutines in-depth analysis Thread 1 // What is

    Coroutine? Task 1 Task 3 Thread 2 Task 2 Task 4 Result
  14. Confidential Kotlin Coroutines in-depth analysis // What are Coroutines Coroutine

    Scope Coroutine Coroutine 1 Coroutine Context Job Dispatcher ... Coroutine N
  15. Confidential Kotlin Coroutines in-depth analysis // What are Coroutines Coroutine

    Scope Coroutine Coroutine 1 Coroutine Context Job Dispatcher ... Coroutine N
  16. Confidential Kotlin Coroutines in-depth analysis // What are Coroutines “Coroutines

    are light-weight threads” Kotlin Doc
  17. Confidential Kotlin Coroutines in-depth analysis // What are Coroutines “Coroutines

    are executors with fancy syntax” Me
  18. Confidential Kotlin Coroutines in-depth analysis // What are Coroutines 1.

    fun foo() = executor.execute { 2. // do something 3. } 1. suspend fun bar() = withContext(Dispatchers.Default) { 2. // do something 3. }
  19. Confidential Kotlin Coroutines in-depth analysis // What are Coroutines 1.

    fun foo() = executor1.execute { 2. // do something on executor1 3. executor2.execute { 4. // do something on executor2 5. executor1.execute { /* do something on executor1 */ } 6. } 7. } 1. suspend fun bar() = withContext(Dispatchers.Default) { 2. // do something 3. }
  20. Confidential Kotlin Coroutines in-depth analysis // What are Coroutines 1.

    fun foo() = executor1.execute { 2. // do something on executor1 3. executor2.execute { 4. // do something on executor2 5. executor1.execute { /* do something on executor1 */ } 6. } 7. } 1. suspend fun bar() = withContext(Dispatchers.Default) { 2. // do something on default dispatcher 3. withContext(Dispatchers.IO) { 4. // do something on IO dispatcher 5. } 6. // do something on default dispatcher 7. }
  21. Confidential Kotlin Coroutines in-depth analysis // What are Coroutines 1.

    fun foo(callback: (String) -> Unit) = executor1.execute { 2. // do something on executor1 3. executor2.execute { 4. // do something on executor2 5. executor1.execute { callback("result") } 6. } 7. } 1. suspend fun bar() = withContext(Dispatchers.Default) { 2. // do something on default dispatcher 3. withContext(Dispatchers.IO) { 4. // do something on IO dispatcher 5. } 6. // do something on default dispatcher 7. }
  22. Confidential Kotlin Coroutines in-depth analysis // What are Coroutines 1.

    fun foo(callback: (String) -> Unit) = executor1.execute { 2. // do something on executor1 3. executor2.execute { 4. // do something on executor2 5. executor1.execute { callback("result") } 6. } 7. } 1. suspend fun bar() = withContext(Dispatchers.Default) { 2. // do something on default dispatcher 3. withContext(Dispatchers.IO) { 4. // do something on IO dispatcher 5. } 6. // do something on default dispatcher 7. return@withContext "result" 8. }
  23. Confidential Kotlin Coroutines in-depth analysis // How do Coroutines work?

    1. fun foo() { 2. 3. } 4. 5. suspend fun bar() { 6. }
  24. Confidential Kotlin Coroutines in-depth analysis // How do Coroutines work?

    1. fun foo() { 2. bar() 3. } 4. 5. suspend fun bar() { 6. } suspend function should be called only from a coroutine or another suspend function
  25. Confidential Kotlin Coroutines in-depth analysis // How do Coroutines work?

    1. fun foo() { 2. GlobalScope.async { 3. bar() 4. } 5. } 6. 7. suspend fun bar() { 8. }
  26. Confidential Kotlin Coroutines in-depth analysis // How do Coroutines work?

    1. fun foo() { 2. GlobalScope.async { 3. bar() 4. } 5. } 6. 7. suspend fun bar() { 8. // before 9. withContext(Dispatchers.IO) { 10. // hard work 11. } 12. // after 13. } Caller Thread Dispatcher.Default Dispatcher.IO Dispatcher.Default Dispatcher.Default Caller Thread
  27. Confidential Kotlin Coroutines in-depth analysis // How do Coroutines work?

  28. Confidential Kotlin Coroutines in-depth analysis // How do Coroutines work?

    1. suspend fun foo() { 2. var text = "before" 3. bar() 4. text = "after" 5. } 1. fun foo(continuation: Continuation<*>) { 2. val exception = continuation.exception 3. when (continuation.label) { 4. 0 -> { 5. if (exception != null) throw exception 6. var text = "before" 7. continuation.L$0 = text 8. continuation.label = 1 9. if (bar() == SUSPENDED) return SUSPENDED 10. } 11. 1 -> { 12. var text = continuation.L$0 13. if (exception != null) throw exception 14. } 15. } 16. var text = "after" 17. }
  29. Confidential Kotlin Coroutines in-depth analysis // How do Coroutines work?

    Coroutine Scope Coroutine Coroutine 1 Coroutine Context Job Dispatcher ... ... Coroutine N Coroutine 1 Coroutine 2 ... Coroutine N
  30. Confidential Kotlin Coroutines in-depth analysis // Coroutine lifecycle Coroutine Scope

    Coroutine Coroutine 1 Coroutine Context Job Dispatcher ... Coroutine N
  31. Confidential Kotlin Coroutines in-depth analysis // Coroutine lifecycle - Job

    1. val job = GlobalScope.launch { 2. // do some stuff 3. }
  32. Confidential Kotlin Coroutines in-depth analysis // Coroutine lifecycle - Job

    1. val job = GlobalScope.launch { 2. // do some stuff 3. } 4. 5. job.invokeOnCompletion { cause -> 6. // job has finished 7. assert(cause == null) 8. assert(job.isCompleted) 9. }
  33. Confidential Kotlin Coroutines in-depth analysis // Coroutine lifecycle - Lazy

    Job 1. val job = GlobalScope.launch(start = CoroutineStart.LAZY) { 2. // do some stuff 3. }
  34. Confidential Kotlin Coroutines in-depth analysis // Coroutine lifecycle - Lazy

    Job 1. val job = GlobalScope.launch(start = CoroutineStart.LAZY) { 2. // do some stuff 3. } 4. 5. job.invokeOnCompletion { cause -> 6. // job has finished 7. assert(cause == null) 8. assert(job.isCompleted) 9. }
  35. Confidential Kotlin Coroutines in-depth analysis // Coroutine lifecycle - Lazy

    Job 1. val job = GlobalScope.launch(start = CoroutineStart.LAZY) { 2. // do some stuff 3. } 4. 5. job.invokeOnCompletion { cause -> 6. // job has finished 7. assert(cause == null) 8. assert(job.isCompleted) 9. } 10. 11. assert(!job.isActive) // new state 12. job.start() 13. assert(job.isActive) // active state
  36. Confidential Kotlin Coroutines in-depth analysis // Coroutine lifecycle - Deferred

    1. val deferred = GlobalScope.async { 2. // do some stuff 3. return@async "result" 4. }
  37. Confidential Kotlin Coroutines in-depth analysis // Coroutine lifecycle - Deferred

    1. val deferred = GlobalScope.async { 2. // do some stuff 3. return@async "result" 4. } 5. 6. deferred.invokeOnCompletion { cause -> 7. // job has finished, we can get the result 8. assert(cause == null) 9. assert(job.isCompleted) 10. assert(deferred.getCompleted() == "result") 11. }
  38. Confidential Kotlin Coroutines in-depth analysis // Coroutine lifecycle - Error

    1. val job = GlobalScope.launch { 2. // oops 3. throw IndexOutOfBoundsException() 4. }
  39. Confidential Kotlin Coroutines in-depth analysis // Coroutine lifecycle - Error

    1. val job = GlobalScope.launch { 2. // oops 3. throw IndexOutOfBoundsException() 4. } 5. 6. job.invokeOnCompletion { cause -> 7. // job has finished with exception 8. assert(job.isCancelled) 9. assert(job.isCompleted) 10. assert(cause is IndexOutOfBoundsException) 11. }
  40. Confidential Kotlin Coroutines in-depth analysis // Coroutine lifecycle - Cancel

    1. val job = GlobalScope.launch { 2. // long operation 3. delay(5000) 4. }
  41. Confidential Kotlin Coroutines in-depth analysis // Coroutine lifecycle - Cancel

    1. val job = GlobalScope.launch { 2. // long operation 3. delay(5000) 4. } 5. 6. job.cancel()
  42. Confidential Kotlin Coroutines in-depth analysis // Coroutine lifecycle - Cancel

    1. val job = GlobalScope.launch { 2. // long operation 3. delay(5000) 4. } 5. 6. job.cancel() 7. 8. job.invokeOnCompletion { cause -> 9. // job has finished with exception 10. assert(job.isCancelled) 11. assert(job.isCompleted) 12. assert(cause is CancellationException) 13. }
  43. Confidential Kotlin Coroutines in-depth analysis Error flow Normal flow //

    Coroutine lifecycle New Active Completing Completed Cancelling Cancelled running waiting for children exception / cancel
  44. Confidential Kotlin Coroutines in-depth analysis // Coroutine lifecycle States isActive

    isCompleted isCancelled New false false false Active true false false Completing true false false Cancelling false false true Cancelled false true true Completed false true false
  45. Confidential Kotlin Coroutines in-depth analysis // Coroutine context Coroutine Scope

    Coroutine Coroutine 1 Coroutine Context Job Dispatcher ... Coroutine N
  46. Confidential Kotlin Coroutines in-depth analysis // Coroutine context Coroutine context

    includes: • Main ◦ Dispatcher { Executors } ◦ Job { Future } • Additional ◦ ThreadContextElement ◦ ContinuationInterceptor ◦ Default exception handlers { Thread.setUncaughtExceptionHandler } • Custom ◦ Any number of custom values { ThreadLocal }
  47. Confidential Kotlin Coroutines in-depth analysis // Coroutine context 1. GlobalScope.launch(Dispatchers.IO)

    { 2. // IO thread pool 3. }
  48. Confidential Kotlin Coroutines in-depth analysis // Coroutine context 1. GlobalScope.launch(Dispatchers.IO)

    { 2. // IO thread pool 3. withContext(Dispatchers.Main) { 4. // Main thread pool 5. } 6. // IO thread pool 7. }
  49. Confidential Kotlin Coroutines in-depth analysis // Coroutine context 1. class

    MyData(val value: String) : AbstractCoroutineContextElement(Key) { 2. object Key : CoroutineContext.Key<MyData> 3. }
  50. Confidential Kotlin Coroutines in-depth analysis // Coroutine context 1. class

    MyData(val value: String) : AbstractCoroutineContextElement(Key) { 2. object Key : CoroutineContext.Key<MyData> 3. } 4. 5. fun foo() { 6. GlobalScope.launch(Dispatchers.IO + MyData("test")) { 7. 8. 9. } 10. }
  51. Confidential Kotlin Coroutines in-depth analysis // Coroutine context 1. class

    MyData(val value: String) : AbstractCoroutineContextElement(Key) { 2. object Key : CoroutineContext.Key<MyData> 3. } 4. 5. fun foo() { 6. GlobalScope.launch(Dispatchers.IO + MyData("test")) { 7. val data = coroutineContext[MyData.Key] as MyData 8. assert(data.value == "test") 9. } 10. }
  52. Confidential Kotlin Coroutines in-depth analysis // Coroutine context 1. class

    MyData(val value: String) : AbstractCoroutineContextElement(Key) { 2. object Key : CoroutineContext.Key<MyData> 3. } 4. 5. fun foo() { 6. GlobalScope.launch(Dispatchers.IO) { 7. assert(coroutineContext[MyData.Key] == null) 8. 9. withContext(MyData("test")) { 10. assert(coroutineContext[MyData.Key] != null) 11. } 12. 13. assert(coroutineContext[MyData.Key] == null) 14. } 15. }
  53. Confidential Kotlin Coroutines in-depth analysis // Coroutine context [Example] 1.

    class Benchmark : AbstractCoroutineContextElement(Key) { 2. object Key : CoroutineContext.Key<Benchmark> 3. 4. val map = ConcurrentHashMap<String, AtomicLong>() 5. }
  54. Confidential Kotlin Coroutines in-depth analysis // Coroutine context [Example] 1.

    class Benchmark : AbstractCoroutineContextElement(Key) { 2. object Key : CoroutineContext.Key<Benchmark> 3. 4. val map = ConcurrentHashMap<String, AtomicLong>() 5. } 6. 7. suspend inline fun <T> benchmark(name: String, block: () -> T): T { 8. val time = System.currentTimeMillis() 9. try { 10. return block() 11. } finally { 12. (coroutineContext[Benchmark.Key] as Benchmark).map 13. .getOrPut(name) { AtomicLong() } 14. .addAndGet(System.currentTimeMillis() - time) 15. } 16. }
  55. Confidential Kotlin Coroutines in-depth analysis // Coroutine context [Example] 1.

    suspend fun fetchData() = benchmark("fetchData") { 2. return@benchmark "result" 3. }
  56. Confidential Kotlin Coroutines in-depth analysis // Coroutine context [Example] 1.

    suspend fun fetchData() = benchmark("fetchData") { 2. return@benchmark "result" 3. } 4. 5. fun main() { 6. val benchmark = Benchmark() 7. 8. GlobalScope.launch(benchmark) { 9. fetchData() 10. } 11. }
  57. Confidential Kotlin Coroutines in-depth analysis // Coroutine context [Example] 1.

    suspend fun fetchData() = benchmark("fetchData") { 2. return@benchmark "result" 3. } 4. 5. fun main() { 6. val benchmark = Benchmark() 7. 8. GlobalScope.launch(benchmark) { 9. fetchData() 10. 11. // log benchmarks 12. System.out.println(benchmark.map) 13. } 14. }
  58. Confidential Kotlin Coroutines in-depth analysis // Coroutine context [Example] 02-27

    12:17:44.402 ? D/Benchmark: benchmark results: CmsManagerCentral.Epg_FetchFavoriteChannels 90.868 ms EpgChannelsApi_Combined.get 0.071 ms EpgChannelsApi.get 0.005 ms EpgFavoritesApi_Combined.fetch 85.428 ms EpgFavoritesApi_Cms.fetch 73.104 ms EpgFavoritesApi_Memory.fetch 0.016 ms EpgFavoritesApi_Persistent.fetch 2.870 ms EpgFavoritesApi_Persistent.fetch (inner) 2.865 ms
  59. Confidential Kotlin Coroutines in-depth analysis // Coroutine dispatchers Coroutine Scope

    Coroutine Coroutine 1 Coroutine Context Job Dispatcher ... Coroutine N
  60. Confidential Kotlin Coroutines in-depth analysis // Coroutine dispatchers 1. GlobalScope.launch(Dispatchers.IO)

    { 2. // IO thread pool 3. withContext(Dispatchers.Main) { 4. // Main thread pool 5. } 6. } 1. ioExecutor.execute { 2. // IO thread pool 3. mainExecutor.execute { 4. // Main thread pool 5. } 6. }
  61. Confidential Kotlin Coroutines in-depth analysis // Coroutine dispatchers 1. abstract

    class CoroutineDispatcher : ContinuationInterceptor { 2. /** 3. * Returns `true` if execution shall be dispatched onto another thread. 4. */ 5. open fun isDispatchNeeded(context: CoroutineContext) = true 6. 7. /** 8. * Dispatches execution of a runnable [block] onto another thread in the given [context]. 9. */ 10. abstract fun dispatch(context: CoroutineContext, block: Runnable) 11. }
  62. Confidential Kotlin Coroutines in-depth analysis // Coroutine dispatchers [Example] 1.

    object MainThreadDispatcher : CoroutineDispatcher() { 2. private val handler = Handler(Looper.getMainLooper()) 3. 4. override fun dispatch(context: CoroutineContext, block: Runnable) { 5. handler.post(block) 6. } 7. }
  63. Confidential Kotlin Coroutines in-depth analysis // Coroutine dispatchers [Example] 1.

    object MainThreadDispatcher : CoroutineDispatcher() { 2. private val handler = Handler(Looper.getMainLooper()) 3. 4. override fun dispatch(context: CoroutineContext, block: Runnable) { 5. handler.post(block) 6. } 7. 8. override fun isDispatchNeeded(context: CoroutineContext): Boolean { 9. return !handler.looper.isCurrentThread() 10. } 11. }
  64. Confidential Kotlin Coroutines in-depth analysis // Coroutine dispatchers [Example] 1.

    object MainThreadDispatcher : CoroutineDispatcher() { 2. private val handler = Handler(Looper.getMainLooper()) 3. 4. override fun dispatch(context: CoroutineContext, block: Runnable) { 5. handler.post(block) 6. } 7. 8. override fun isDispatchNeeded(context: CoroutineContext): Boolean { 9. return !handler.looper.isCurrentThread() 10. } 11. } 12. 13. GlobalScope.launch(MainThreadDispatcher) { 14. // runs on Android's main thread 15. }
  65. Confidential Kotlin Coroutines in-depth analysis // Child coroutines Coroutine Scope

    Coroutine Coroutine 1 Coroutine Context Job Dispatcher ... Coroutine N
  66. Confidential Kotlin Coroutines in-depth analysis // Child coroutines - Starting

    1. suspend fun slowRandom(max: Long): Long { 2. Thread.sleep(1000) 3. return Random.nextLong(max) 4. }
  67. Confidential Kotlin Coroutines in-depth analysis // Child coroutines - Starting

    1. suspend fun slowRandom(max: Long): Long { 2. delay(1000) 3. return Random.nextLong(max) 4. }
  68. Confidential Kotlin Coroutines in-depth analysis // Child coroutines - Starting

    1. suspend fun slowRandom(max: Long): Long { 2. delay(1000) 3. return Random.nextLong(max) 4. } 5. 6. suspend fun combine(): Long { 7. val value1 = slowRandom(100) 8. val value2 = slowRandom(200) 9. return value1 + value2 10. }
  69. Confidential Kotlin Coroutines in-depth analysis // Child coroutines - Starting

    1. suspend fun slowRandom(max: Long): Long { 2. delay(1000) 3. return Random.nextLong(max) 4. } 5. 6. suspend fun combine(): Long { 7. val value1 = GlobalScope.async { slowRandom(100) } 8. val value2 = GlobalScope.async { slowRandom(200) } 9. return value1.await() + value2.await() 10. }
  70. Confidential Kotlin Coroutines in-depth analysis // Child coroutines - Starting

    1. suspend fun slowRandom(max: Long): Long { 2. delay(1000) 3. return Random.nextLong(max) 4. } 5. 6. suspend fun combine(): Long { 7. val value1 = GlobalScope.async(coroutineContext) { slowRandom(100) } 8. val value2 = GlobalScope.async(coroutineContext) { slowRandom(200) } 9. return value1.await() + value2.await() 10. }
  71. Confidential Kotlin Coroutines in-depth analysis // Child coroutines - Starting

    1. suspend fun slowRandom(max: Long): Long { 2. delay(1000) 3. return Random.nextLong(max) 4. } 5. 6. suspend fun combine() = coroutineScope { 7. val value1 = async { slowRandom(100) } 8. val value2 = async { slowRandom(200) } 9. return@coroutineScope value1.await() + value2.await() 10. }
  72. Confidential Kotlin Coroutines in-depth analysis // Child coroutines - Starting

    1. suspend fun slowRandom(max: Long): Long { 2. delay(1000) 3. return Random.nextLong(max) 4. } 5. 6. suspend fun combine() = supervisorScope { 7. val value1 = async { slowRandom(100) } 8. val value2 = async { slowRandom(200) } 9. return@supervisorScope value1.await() + value2.await() 10. }
  73. Confidential Kotlin Coroutines in-depth analysis // Child coroutines - Ending

    1. val job = GlobalScope.launch { 2. 3. GlobalScope.launch { 4. delay(1000) 5. System.out.println("child end") 6. } 7. } 8. 9. job.invokeOnCompletion { 10. System.out.println("parent end") 11. } Output -> parent end, child end
  74. Confidential Kotlin Coroutines in-depth analysis // Child coroutines - Ending

    1. val job = GlobalScope.launch { 2. 3. GlobalScope.launch(coroutineContext) { 4. delay(1000) 5. System.out.println("child end") 6. } 7. } 8. 9. job.invokeOnCompletion { 10. System.out.println("parent end") 11. } Output -> child end, parent end
  75. Confidential Kotlin Coroutines in-depth analysis // Child coroutines - Cancellation

    1. val job = GlobalScope.launch { 2. 3. GlobalScope.launch { 4. delay(1000) 5. System.out.println("child end") 6. } 7. } 8. 9. job.invokeOnCompletion { 10. System.out.println("parent end") 11. } 12. 13. job.cancel() Output -> parent end, child end
  76. Confidential Kotlin Coroutines in-depth analysis // Child coroutines - Cancellation

    1. val job = GlobalScope.launch { 2. 3. GlobalScope.launch(coroutineContext) { 4. delay(1000) 5. System.out.println("child end") 6. } 7. } 8. 9. job.invokeOnCompletion { 10. System.out.println("parent end") 11. } 12. 13. job.cancel() Output -> parent end
  77. Confidential Kotlin Coroutines in-depth analysis // Coroutine scope Coroutine Scope

    Coroutine Coroutine 1 Coroutine Context Job Dispatcher ... Coroutine N
  78. Confidential Kotlin Coroutines in-depth analysis // Coroutine scope 1. GlobalScope.launch(Dispatchers.IO)

    { 2. // something interesting 3. }
  79. Confidential Kotlin Coroutines in-depth analysis // Coroutine scope 1. object

    GlobalScope : CoroutineScope { 2. override val coroutineContext: CoroutineContext 3. get() = EmptyCoroutineContext 4. } 1. public interface CoroutineScope { 2. /** 3. * Context of this scope. 4. */ 5. public val coroutineContext: CoroutineContext 6. }
  80. Confidential Kotlin Coroutines in-depth analysis // Coroutine scope 1. public

    fun CoroutineScope.launch( 2. context: CoroutineContext = EmptyCoroutineContext, 3. start: CoroutineStart = CoroutineStart.DEFAULT, 4. block: suspend CoroutineScope.() -> Unit 5. ): Job 1. public fun <T> CoroutineScope.async( 2. context: CoroutineContext = EmptyCoroutineContext, 3. start: CoroutineStart = CoroutineStart.DEFAULT, 4. block: suspend CoroutineScope.() -> T 5. ): Deferred<T>
  81. Confidential Kotlin Coroutines in-depth analysis // Coroutine scope 1. class

    MainActivity : Activity(), CoroutineScope { 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. }
  82. Confidential Kotlin Coroutines in-depth analysis // Coroutine scope 1. class

    MainActivity : Activity(), CoroutineScope { 2. 3. 4. override val coroutineContext = Dispatchers.Main 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. }
  83. Confidential Kotlin Coroutines in-depth analysis // Coroutine scope 1. class

    MainActivity : Activity(), CoroutineScope { 2. private val job = Job() 3. 4. override val coroutineContext = Dispatchers.Main + job 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. }
  84. Confidential Kotlin Coroutines in-depth analysis // Coroutine scope 1. class

    MainActivity : Activity(), CoroutineScope { 2. private val job = Job() 3. 4. override val coroutineContext = Dispatchers.Main + job 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. override fun onDestroy() { 15. super.onDestroy() 16. 17. job.cancel() 18. } 19. }
  85. Confidential Kotlin Coroutines in-depth analysis // Coroutine scope 1. class

    MainActivity : Activity(), CoroutineScope { 2. private val job = Job() 3. 4. override val coroutineContext = Dispatchers.Main + job 5. 6. override fun onCreate() { 7. super.onCreate() 8. 9. launch { 10. // do something 11. } 12. } 13. 14. override fun onDestroy() { 15. super.onDestroy() 16. 17. job.cancel() 18. } 19. }
  86. Confidential Kotlin Coroutines in-depth analysis // Coroutine scope - Unsupervised

    1. coroutineScope { 2. val job1 = async { throw Exception() } 3. val job2 = async { delay(1000) } 4. try { 5. job1.await() 6. System.out.println("job1 finished") 7. } catch (e: Exception) { 8. System.out.println("job1 crashed -> $e") 9. } 10. try { 11. job2.await() 12. System.out.println("job2 finished") 13. } catch (e: Exception) { 14. System.out.println("job2 crashed -> $e") 15. } 16. } job1 crashed: java.lang.Exception job2 crashed: JobCancellationException: Parent job is Cancelling
  87. Confidential Kotlin Coroutines in-depth analysis // Coroutine scope - Supervised

    1. supervisorScope { 2. val job1 = async { throw Exception() } 3. val job2 = async { delay(1000) } 4. try { 5. job1.await() 6. System.out.println("job1 finished") 7. } catch (e: Exception) { 8. System.out.println("job1 crashed -> $e") 9. } 10. try { 11. job2.await() 12. System.out.println("job2 finished") 13. } catch (e: Exception) { 14. System.out.println("job2 crashed -> $e") 15. } 16. } job1 crashed -> java.lang.Exception job2 finished
  88. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines

  89. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines 1. object

    ExternalApi { 2. fun getUser(callback: (User) -> Unit) 3. }
  90. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines 1. object

    ExternalApi { 2. fun getUser(callback: (User) -> Unit) 3. } 4. 5. suspend fun getUser(): User { 6. 7. 8. 9. 10. 11. 12. }
  91. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines 1. object

    ExternalApi { 2. fun getUser(callback: (User) -> Unit) 3. } 4. 5. suspend fun getUser(): User { 6. return suspendCoroutine { continuation -> 7. // non-suspend block here 8. 9. 10. 11. } 12. }
  92. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines 1. object

    ExternalApi { 2. fun getUser(callback: (User) -> Unit) 3. } 4. 5. suspend fun getUser(): User { 6. return suspendCoroutine { continuation -> 7. // non-suspend block here 8. ExternalApi.getUser { user -> 9. 10. } 11. } 12. }
  93. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines 1. object

    ExternalApi { 2. fun getUser(callback: (User) -> Unit) 3. } 4. 5. suspend fun getUser(): User { 6. return suspendCoroutine { continuation -> 7. // non-suspend block here 8. ExternalApi.getUser { user -> 9. continuation.resume(user) 10. } 11. } 12. }
  94. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines 1. object

    ExternalApi { 2. fun getUser(callback: (User) -> Unit) 3. } 4. 5. suspend fun getUser(): User { 6. return suspendCoroutine { continuation -> 7. // non-suspend block here 8. ExternalApi.getUser { user -> 9. continuation.resume(user) 10. } 11. } 12. } 13. 14. suspend fun showUser() { 15. val user = getUser() 16. // ... 17. }
  95. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines - Cancel

    1. object ExternalApi { 2. fun getUser(callback: (User) -> Unit): Request 3. } 4.
  96. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines - Cancel

    1. object ExternalApi { 2. fun getUser(callback: (User) -> Unit): Request 3. } 4. 5. suspend fun getUser(): User { 6. 7. 8. 9. 10. 11. 12. 13. 14. }
  97. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines - Cancel

    1. object ExternalApi { 2. fun getUser(callback: (User) -> Unit): Request 3. } 4. 5. suspend fun getUser(): User { 6. return suspendCancellableCoroutine { continuation -> 7. 8. 9. 10. 11. 12. 13. } 14. }
  98. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines - Cancel

    1. object ExternalApi { 2. fun getUser(callback: (User) -> Unit): Request 3. } 4. 5. suspend fun getUser(): User { 6. return suspendCancellableCoroutine { continuation -> 7. val request = ExternalApi.getUser { user -> 8. continuation.resume(user) 9. } 10. 11. 12. 13. } 14. }
  99. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines - Cancel

    1. object ExternalApi { 2. fun getUser(callback: (User) -> Unit): Request 3. } 4. 5. suspend fun getUser(): User { 6. return suspendCancellableCoroutine { continuation -> 7. val request = ExternalApi.getUser { user -> 8. continuation.resume(user) 9. } 10. continuation.invokeOnCancellation { 11. request.cancel() 12. } 13. } 14. }
  100. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines - Errors

    1. object ExternalApi { 2. fun getUser(callback: (User?) -> Unit) 3. } 4.
  101. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines - Errors

    1. object ExternalApi { 2. fun getUser(callback: (User?) -> Unit) 3. } 4. 5. suspend fun getUser(): User { 6. return suspendCoroutine { continuation -> 7. ExternalApi.getUser { user -> 8. if (user != null) { 9. continuation.resume(user) 10. } 11. 12. 13. } 14. } 15. }
  102. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines - Errors

    1. object ExternalApi { 2. fun getUser(callback: (User?) -> Unit) 3. } 4. 5. suspend fun getUser(): User { 6. return suspendCoroutine { continuation -> 7. ExternalApi.getUser { user -> 8. if (user != null) { 9. continuation.resume(user) 10. } else { 11. continuation.resumeWithException(Exception("no user")) 12. } 13. } 14. } 15. }
  103. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines - Errors

    1. suspend fun showUser() { 2. try { 3. val user = getUser() 4. // ... 5. } catch (e: Exception) { 6. e.printStackTrace() 7. } 8. }
  104. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines [Example] 1.

    fun getUser(): Single<User>
  105. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines [Example] 1.

    fun getUser(): Single<User> 2. 3. suspend fun <T> Single<T>.await(): User { 4. 5. 6. 7. 8. 9. 10. 11. 12. }
  106. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines [Example] 1.

    fun getUser(): Single<User> 2. 3. suspend fun <T> Single<T>.await(): User { 4. return suspendCancellableCoroutine { continuation -> 5. 6. 7. 8. 9. 10. 11. } 12. }
  107. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines [Example] 1.

    fun getUser(): Single<User> 2. 3. suspend fun <T> Single<T>.await(): User { 4. return suspendCancellableCoroutine { continuation -> 5. getUser().subscribe({ user -> 6. continuation.resume(user) 7. }) 8. 9. 10. 11. } 12. }
  108. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines [Example] 1.

    fun getUser(): Single<User> 2. 3. suspend fun <T> Single<T>.await(): User { 4. return suspendCancellableCoroutine { continuation -> 5. getUser().subscribe({ user -> 6. continuation.resume(user) 7. }, { error -> 8. continuation.resumeWithException(error) 9. }) 10. 11. } 12. }
  109. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines [Example] 1.

    fun getUser(): Single<User> 2. 3. suspend fun <T> Single<T>.await(): User { 4. return suspendCancellableCoroutine { continuation -> 5. val disposable = getUser().subscribe({ user -> 6. continuation.resume(user) 7. }, { error -> 8. continuation.resumeWithException(error) 9. }) 10. continuation.invokeOnCancellation { disposable.dispose() } 11. } 12. }
  110. Confidential Kotlin Coroutines in-depth analysis // Suspended coroutines [Example] 1.

    fun getUser(): Single<User> 2. 3. suspend fun showUser() { 4. try { 5. val user = getUser().await() 6. // ... 7. } catch (e: Exception) { 8. e.printStackTrace() 9. } 10. }
  111. Confidential Kotlin Coroutines in-depth analysis // Testing Coroutines

  112. Confidential Kotlin Coroutines in-depth analysis // Testing Coroutines 1. suspend

    fun mySuspendFunction(): String { 2. return "My Test String" 3. } 4. 5. @Test 6. fun mySuspendFunctionTest() { 7. 8. }
  113. Confidential Kotlin Coroutines in-depth analysis // Testing Coroutines 1. suspend

    fun mySuspendFunction(): String { 2. return "My Test String" 3. } 4. 5. @Test 6. fun mySuspendFunctionTest() { 7. val job = GlobalScope.async { 8. mySuspendFunction() 9. } 10. job.invokeOnCompletion { 11. assertEquals(job.getCompleted(), "My Test String") 12. } 13. }
  114. Confidential Kotlin Coroutines in-depth analysis // Testing Coroutines 1. suspend

    fun mySuspendFunction(): String { 2. return "My Test String" 3. } 4. 5. @Test 6. fun mySuspendFunctionTest() { 7. 8. }
  115. Confidential Kotlin Coroutines in-depth analysis // Testing Coroutines 1. suspend

    fun mySuspendFunction(): String { 2. return "My Test String" 3. } 4. 5. @Test 6. fun mySuspendFunctionTest() { 7. val result = runBlocking { 8. mySuspendFunction() 9. } 10. assertEquals(result, "My Test String") 11. }
  116. Confidential Kotlin Coroutines in-depth analysis // Testing Coroutines 1. suspend

    fun mySuspendFunction(): String { 2. return "My Test String" 3. } 4. 5. @Test 6. fun mySuspendFunctionTest() = runBlocking { 7. assertEquals(mySuspendFunction(), "My Test String") 8. }
  117. Confidential Kotlin Coroutines in-depth analysis // Benchmarks

  118. Confidential Kotlin Coroutines in-depth analysis // Benchmarks 1. for (i

    in 0..1_000_000) { 2. launch(dispatcher) { 3. System.out.println("$i") 4. } 5. } 1. for (i in 0..1_000_000) { 2. executor.execute { 3. System.out.println("$i") 4. } 5. } finished in 4.219 sec finished in 3.853 sec ~10% overhead
  119. Confidential Kotlin Coroutines in-depth analysis // Final thoughts

  120. Confidential Kotlin Coroutines in-depth analysis // Final thoughts 1. Sequences

    and channels were not covered 2. Reflections don’t work with coroutines 3. No way to call coroutines from Java (without kotlin wrapper) 4. Coroutines debugger bugs: a. Variables inside coroutine are not visible b. Evaluate expression doesn’t work c. Breakpoint after last suspension point doesn’t trigger
  121. // Obligatory Q&A

  122. // Thank you.