Simple Flow Example error Load Friends from Network Ask for Permissions Contact permissions? Loading Show Retry Dialog retry? yes no Network Error success Canceled Granted? Read Contacts Show Results yes no yes no
We’ve gotten okay at this... - Chaining Steps - Hard to read and maintain - Callbacks - Callback hell, hard to refactor, no cancellation - Promises / Futures - CompletableFuture minSdk 24, no cancellation, concurrent by default - Observables / Reactive Streams - Steep learning curve, different paradigm, too many operators
“Experimental” - Stable (as in not-buggy) - API is not finalized - Guaranteed backwards compatibility - Calls covered here are unlikely to change - IDE tools to ease migration Can I use it in production? “Yes! You should.” - Roman Elizarov (seconded by me)
Creating Coroutines suspend fun delayedParseInt(i: String): Int { return suspendCoroutine { cont -> // this code gets executed immediately to set up the coroutine Thread { Thread.sleep(1000) // some time later, use the continuation to resume from suspension try { val result = i.toInt() cont.resume(result) }catch (e: NumberFormatException) { cont.resumeWithException(e) } }.start() // after leaving this method, the caller will suspend until resume is called } }
Combining Coroutines suspend fun doFirst(): String suspend fun doSecond(): Boolean suspend fun doThird(data: String) suspend fun example() { val data = doFirst() if (doSecond()) { doThird(data) } }
Coroutine Context Where the non-suspending code is run. Or another way: What thread picks back up after a suspension resumes. - UI - CommonPool - newSingleThreadContext(name = "single") - newFixedThreadPoolContext(nThreads = 3, name = "pool")
Concurrency - Async/Await val one = async(CommonPool) { delay(100) return@async 1 } // one is a Promise (Deferred). It has already started val two = async(CommonPool) { delay(100) return@async 2 } // now both promises are running // await() suspends until the promise resolves val sum = one.await() + two.await()
Need to be Cancelled override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) launch(UI) { var timer = 0 while (true) { textView.text = "$timer seconds" delay(1, TimeUnit.SECONDS) timer++ } } } // will throw IllegalStateException: Activity has been destroyed
Cancellation is an Exception val job = launch(UI) { try { dangerousMethod() // throws CancellationException } catch (e: CancellationException){ throw e // rethrow, to be handled by the coroutine context } catch (e: Exception) { Log.w("EXAMPLE", "dangerous method exploded") } doTheNextThing() // next thing won't get called anymore } job.cancel()