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

IDCamp Sharing Session - Kotlin Coroutines

IDCamp Sharing Session - Kotlin Coroutines

Avatar for Eko Suhariyadi

Eko Suhariyadi

November 24, 2019

More Decks by Eko Suhariyadi

Other Decks in Programming

Transcript

  1. Disclaimer • Not a Deep Dive • Won’t cover Coroutines

    Flow • Won’t cover Coroutines Channel
  2. Asynchronous Programming? • Thread • eg: RxJava • Thread is

    costly to create • Limited by processor core • Coroutine • It is a ‘concept’ to allow execution of program to be suspended and resumed • Live inside thread -> a single Thread can have many coroutines • Mostly, languages have their own implementation • goroutine • Kotlin coroutines
  3. What are Coroutine? “A coroutine is an instance of suspendable

    computation, conceptually similar to a thread, in the sense that it takes a block of code to run and has a similar life-cycle, it is created and started, but it is not bound to any particular thread. It may suspend its execution in one thread and resume in another one. Moreover, like a future or promise, it may complete with some result or exception.”
  4. Kotlin Coroutines Building Block • CoroutineDispatcher • Determine which Thread

    that a coroutine is running • Similar to RxJava’s Schedulers • CoroutineContext • Contain set of elements that represent a coroutine, such as • Dispatcher • Job, a component that hold coroutine execution. 
 We can doing cancellation of coroutine via Job • CoroutineScope • Hold coroutines Context and Dispatcher • Scoping coroutines execution, to make it easier with cancellation
  5. Dispatchers • Dispatchers.Default • Default dispatcher used by standard coroutine

    builders • Dispatchers.IO • Designed for I/O task, such as networking, files • Dispatchers.Main • Confined execution in Main Thread (UI) • Dispatchers.Unconfined • Using the same thread by the caller, and might switch to another thread when have corresponding suspended function
  6. Kotlin Coroutines Standard Builders • launch • Fire and forget,

    just launch an execution and don’t care about the return value • Kind of similar to RxJava Completable • async • Launch execution and await for the return value • Kind of similar to RxJava Single • runBlocking • Launch execution that behave like sequential process • withContext • Used to switch execution into another CoroutineContext (*Thread)
  7. Your first Coroutine fun main() { GlobalScope.launch { delay(1_000) println("World!")

    } print(“Hello, ") Thread.sleep(2_000) } // it will print // Hello, World! We need a scope to launch coroutine Without this line it will just print Hello Because that GlobalScope coroutine is running (suspended) in background
  8. Your first Coroutine fun main() { GlobalScope.launch { delay(1_000) println("World!")

    } print(“Hello, ") Thread.sleep(2_000) } // it will print // Hello, World!
  9. Your first Coroutine fun main() { GlobalScope.launch { delay(1_000) println("World!")

    } print(“Hello, “) runBlocking { delay(2_000) } }
 
 // it will print
 // Hello, World Blocking coroutine To make it coroutines friendly than using Thread.sleep
  10. Your first Coroutine fun main() { GlobalScope.launch { delay(1_000) println("World!")

    } print(“Hello, “) runBlocking { delay(2_000) } }
 
 // it will print
 // Hello, World
  11. Your first Coroutine fun main() = runBlocking { GlobalScope.launch {

    delay(1_000) println("World!") } print("Hello, ") delay(2_000) }
 
 // it will print // Hello, World!
  12. Coroutine’s Job • When we execute a coroutine, it will

    return a Job object • Kind of similar with RxJava’s Disposable • Job.join() • To wait until a Job is complete • Job.cancel() • To cancel a Job execution • It will throw CancellationException, 
 but swallowed by coroutines as default behaviour
  13. Coroutine’s Job fun main() = runBlocking { val job =

    GlobalScope.launch { delay(1_000) println("World!") } print("Hello, ") job.join() }
 
 // it will print // Hello, World!
  14. Coroutine’s Job fun main() = runBlocking { val job =

    GlobalScope.launch { delay(1_000) println("World!") } print("Hello, ") job.cancel() }
 
 // it will print
 // Hello,
  15. Structured Concurrency • GlobalScope.launch will create top-level coroutine • When

    we have many coroutines of GlobalScope.
 To handle join or cancel of all of them manually is error-prone. • This is might lead into potential memory leak • So, avoid using GlobalScope
  16. Structured Concurrency fun main() = runBlocking { val job =

    GlobalScope.launch { delay(1_000) println("World!") } print("Hello, ") job.join() }
 
 // it will print // Hello, World!
  17. Structured Concurrency fun main() = runBlocking { launch { delay(1_000)

    println("World!") } print("Hello, ") }
 
 // it will print // Hello, World! It will launch coroutine using runBlocking’s scope It will create parent-child relation between jobs The coroutine lifecycle (cancel, join) will be handled by runBlocking’s scope
  18. Structured Concurrency Scope Builder fun main() = runBlocking { launch

    { delay(200) println("task in runBlocking") } coroutineScope { launch { delay(500) println("task in nested launch of coroutine scope") } delay(100) println("task in coroutine scope") } println("coroutine scope is over") }
 
 // it will print
 // task in coroutine scope
 // task in runBlocking
 // task in nested launch of coroutine scope
 // coroutine scope is over It will create a new scope The new scope won’t complete until all child complete
  19. Suspending Function fun main() = runBlocking { launch { world()

    } print("Hello, ") } suspend fun world() { delay(1_000) println(“World!") }
 
 // it will print // Hello, World!
  20. Suspending Function fun main() = runBlocking { world() print("Hello, ")

    } suspend fun world() { delay(1_000) println(“World!") }
 
 // it will print
 // World! // Hello, 
 With suspend modifier
 we can make a function
 can be called inside coroutine block But will be executed sequentially
  21. Suspending Function fun main() = runBlocking { launch { world()

    } print("Hello, ") } suspend fun world() { delay(1_000) println(“World!") }
 
 // it will print // Hello, World!
  22. Suspending Function fun main() = runBlocking { val world =

    async { world() } print("Hello, ${world.await()}") } suspend fun world(): String { delay(1_000) return "World!" }
  23. Supervision fun main() = runBlocking { val parent = Job()

    with(CoroutineScope(coroutineContext + parent)) { val job1 = launch { delay(300) println("Job1 executed”) } val job2 = launch(CoroutineExceptionHandler { _, _ -> }) { delay(100) println("Job2 throws exception") throw IllegalAccessException() } joinAll(job1, job2) } println("parent is cancelled: ${parent.isCancelled}") }
 // it will print
 // Job2 throws exception
 // parent is cancelled: true
 When child job throws exception parent job will be cancelled
  24. Supervision fun main() = runBlocking { val parent = Job()

    with(CoroutineScope(coroutineContext + SupervisorJob(parent))) { val job1 = launch { delay(300) println("Job1 executed") } val job2 = launch(CoroutineExceptionHandler { _, _ -> }) { delay(100) println("Job2 throws exception") throw IllegalAccessException() } joinAll(job1, job2) } println("parent is cancelled: ${parent.isCancelled}") }
 // it will print
 // Job2 throws exception
 // Job1 executed
 // parent is cancelled: false With SupervisorJob when child job throws exception parent job won’t be cancelled