Since the release of Kotlin 1.1 there is now the language feature of Kotlin Coroutines available for use in Java and Android projects. Coroutines are a new way to write asynchronous and non-blocking code. They can be thought of as light-weight threads without having to deal with all the problems that threads bring to the table.
A lot of developers think that Kotlin Coroutines are mainly or only useful for Kotlin on the JVM, but that’s not true. There are a variety of use cases in which the application of Coroutines can make a lot of sense on Android.
This talk is introducing the ideas behind Kotlin Coroutines, showing how to use them in Kotlin code for both the JVM and Android via the kotlinx-coroutines APIs and then exploring specific applications in Android. Part of this is a deeper look into the use of Coroutines in higher-level frameworks such as AsyncAwait and Anko and how they stack up against other concepts of asynchrony such as rxJava.
2. #droidconVN
HELLO
HELLO
My name is Kai!
Software Architect in the web and mobile (Android) space
from New Zealand.
Stuff I enjoy: Android, Kotlin, CFML, compilers and parsers, aviation and flying
small aircraft, cats and chickens, Nintendo video games of all ages!
Follow me on Twitter: @AgentK
4. KOTLIN IN 3 MINUTES
https://www.flickr.com/photos/tschiae/8080742303/
5. #droidconVN
HISTORY OF KOTLIN (I)
▸ Jetbrains wanted a more efficient JVM language when building products
▸ Looked at Scala, Groovy, etc. but came up with their own language spec
▸ First shown at the JVM Language Summit in 2011
▸ Got some traction in Android-land in 2014
▸ Modern language features (lambdas, HOF etc) but Java 6 byte code
▸ Low methods count (~7000)
▸ Strong focus on concision and an efficient, bloat-free language
KOTLIN IN 3 MINUTES
6. #droidconVN
HISTORY OF KOTLIN (II)
▸ Since 1.0 release in early 2016:
▸ multiple maintenance releases
▸ now at 1.0.7
▸ 1.1 was released in Feb 2017,
▸ currently at 1.1.60
▸ experimental new feature: Kotlin coroutines
KOTLIN IN 3 MINUTES
7. #droidconVN
HISTORY OF KOTLIN (III)
▸ At Google IO 2017, Kotlin became a first-grade language for Android
▸ Release of 1.2 happened in late 2017 (CHECK)
▸ Java 9 compatibility
▸ Multiplatform projects
▸ Huge improvements to StdLib, type inference, smart cast etc.
▸ Strong community, lots of interesting frameworks, awesome support from
Jetbrains
KOTLIN IN 3 MINUTES
8. #droidconVN
KOTLIN IN 5 MINUTES
PROMINENT LANGUAGE FEATURES
▸ Immutability
▸ String templates
▸ Explicit null handling
▸ Properties and Fields
▸ Data classes
▸ Extension functions
▸ Syntactic sugar
▸ Type inference
▸ Lambdas
▸ Collection API
▸ Type-safe builders
▸ Java-Kotlin-Interop
10. #droidconVN
WHY COROUTINES?
MOTIVATION
▸ Asynchronous programming is becoming increasingly important
▸ Problem: the need to avoid blocking introduces a lot of complexity
▸ Threads are:
▸ expensive to manage and limited
▸ complex in applications with lots of mutable state
▸ usually even more complex to deal with in UI-driven applications
▸ Callback hell (very common in Javascript)
12. #droidconVN
WHY COROUTINES?
HISTORY
▸ Coroutines were first mentioned and used in Simula 67
▸ detach & resume keywords to suspend and then later resume execution
▸ Pushed aside by industry trend towards multi-threading
▸ C# has async/await (language-level keywords)
▸ Go was one of the first modern languages re-introducing coroutines
13. #droidconVN
WHY COROUTINES?
COROUTINES IN KOTLIN (I)
▸ Kotlin’s approach: Suspending functions
▸ Function/lambda that can be suspended and resumed
▸ Requires no context-switching on the OS level
▸ Minimal integration into the core language and stdlib, most of functionality
provided by libraries
▸ Design allows for a variety of asynchronous API methodologies to be
implemented on top of Kotlin coroutines
▸ Supposed to feel like writing traditional code
14. #droidconVN
WHY COROUTINES?
COROUTINES IN KOTLIN (II)
▸ Easiest way to think about a coroutine is a very light-weighted thread
▸ They can run in parallel
▸ Coroutines can wait for each other
▸ They can communicate with each other
▸ Very, very cheap to create (compared to threads)
▸ A thread can run a lot of coroutines
15. #droidconVN
WHY COROUTINES?
COROUTINES IN KOTLIN (III)
▸ Multiple layers of libraries and integration points:
▸ Language support: suspending functions
▸ Low-level library & generators in kotlin.coroutines
▸ Mid-level library in kotlinx.coroutines
▸ High-level libraries (Anko etc.)
17. #droidconVN
USING COROUTINES IN YOUR KOTLIN CODE
FUNDAMENTALS
▸ Kotlin 1.1.4 is the absolute minimum, better 1.1.50+ or right away latest 1.2
▸ Enable experimental feature in Gradle
kotlin {
experimental {
coroutines 'enable'
}
}
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.22.5'
18. #droidconVN
USING COROUTINES IN YOUR KOTLIN CODE
“HELLO WORLD”
▸ launch {…} starts new coroutine on CommonPool thread pool
▸ delay() is a suspending function (not blocking a thread)
fun main(args: Array<String>) {
println("Start on main thread")
launch(CommonPool) {
delay(5000)
println("Hello from coroutine")
}
println("Hello from main thread")
Thread.sleep(10000)
println("Stop on main thread")
}
19. #droidconVN
USING COROUTINES IN YOUR KOTLIN CODE
“HELLO WORLD” IN BETTER
▸ Wait for the coroutines to finish - launch {…} returns a Job object
fun main(args: Array<String>) = runBlocking {
println("Start on main thread")
val job = launch(CommonPool) {
delay(5000)
println("Hello from coroutine")
}
println("Hello from main thread")
job.join()
println("Stop on main thread")
}
20. #droidconVN
USING COROUTINES IN YOUR KOTLIN CODE
THREADS VS COROUTINES
fun main(args: Array<String>) {
val jobs = List(10_000) {
thread(start = true) {
Thread.sleep(1000L)
print(".")
}
}
jobs.forEach { it.join() }
}
21. #droidconVN
USING COROUTINES IN YOUR KOTLIN CODE
THREADS VS COROUTINES
fun main(args: Array<String>) = runBlocking<Unit> {
val jobs = List(10_000) {
launch(CommonPool) {
delay(1000L)
print(".")
}
}
jobs.forEach { it.join() }
}
22. #droidconVN
USING COROUTINES IN YOUR KOTLIN CODE
ASYNC/AWAIT (I)
▸ async {…} starts new coroutine and returns a Deferred<T> object
▸ Deferred<T>.await() returns result of the coroutine
▸ await() needs to be called from inside a coroutine, because it needs to suspend
in a non-blocking way
▸ Solution: wrap into runBlocking {…} coroutine
▸ Starts coroutine and wait until it’s finished
23. #droidconVN
USING COROUTINES IN YOUR KOTLIN CODE
ASYNC/AWAIT (II)
fun main(args: Array<String>) {
val deferred = (1..60_000).map { n ->
async (CommonPool) {
n
}
}
runBlocking {
val sum = deferred.sumBy { it.await() }
println("Sum: $sum")
}
}
24. #droidconVN
USING COROUTINES IN YOUR KOTLIN CODE
SUSPEND FUNCTIONS (I)
▸ Coroutines can suspend without blocking a thread
▸ Functions that might suspend need to be marked with the suspend keyword
▸ They can only be called from another suspend function or a coroutine
25. #droidconVN
USING COROUTINES IN YOUR KOTLIN CODE
SUSPEND FUNCTIONS (II)
fun main(args: Array<String>) {
val deferred = (1..1_000_000).map { n ->
async (CommonPool) {
doWork(n)
}
}
runBlocking {
...
}
}
suspend fun doWork(n: Int) : Int {
delay(50)
return n
}
26. #droidconVN
USING COROUTINES IN YOUR KOTLIN CODE
BEHIND THE SCENES
▸ Kotlin coroutines are very light on language features:
▸ suspend the only new keyword added to the language and acts pretty much
like a compiler flag
▸ Continuation and CoroutineContext in stdlib
▸ All the rest is in the kotlinx.coroutines library
This makes the design of Kotlin coroutines very composable.
27. #droidconVN
USING COROUTINES IN YOUR KOTLIN CODE
MORE BEHIND THE SCENES
▸ At compilation:
▸ Suspending functions compile to functions with a general callback interface
of type Continuation
▸ Code with suspension points compiles to a state machine
▸ launch, runBlocking, async etc. are often called coroutine builders
28. #droidconVN
USING COROUTINES IN YOUR KOTLIN CODE
LAUNCH VS ASYNC
▸ Conceptually very similar
▸ launch {…} returns a Job, no resulting value
▸ async {…} returns a Deferred - a future that can be used to obtain a value
▸ async generally suited better in situations with independent concurrent flows
29. #droidconVN
USING COROUTINES IN YOUR KOTLIN CODE
WAITING AND CANCELLING
▸ cancel() and then join() can be used to terminate a coroutine execution early
▸ Job has an extension function cancelAndJoin() doing both at once
val job = launch {
repeat(1000) { i ->
println("job: I'm sitting here and delaying $i ...")
delay(500L)
}
}
delay(1300L)
println("main: I'm really over waiting!")
job.cancel()
job.join() // or use: job.cancelAndJoin()
println("main: Let's go.")
30. #droidconVN
USING COROUTINES IN YOUR KOTLIN CODE
DEALING WITH TIMEOUTS
▸ Quite often the motivation for cancelling is a timeout:
▸ Track yourself via the Job instance
▸ Use withTimeout() {…}
withTimeout(1300L) {
repeat(1000) { i ->
println("I'm waiting $i ...")
delay(500L)
}
}
31. #droidconVN
USING COROUTINES IN YOUR KOTLIN CODE
THAT’S NOT ALL OF IT YET
▸ Coroutine contexts
▸ Coroutines and threads
▸ Channels
▸ Pipelines
▸ Dealing with state
▸ Shared (mutable) state & Actors
Compare Coroutines with Java Futures API
33. #droidconVN
MOTIVATION
▸ In general, UI-driven apps need to be aware of long-running processes
▸ Android specifically:
▸ we can’t do any networking on the UI thread
▸ we need to avoid long-running operations due to ANRs
▸ The kotlinx.coroutines library by Jetbrains provides a starting point for
Android, too.
LIBRARIES AND COROUTINES ON ANDROID
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.22.5'
34. #droidconVN
UI CONTEXT
▸ The Android library provides access to a coroutine context for the UI thread
▸ Coroutine launches in UI thread, UI updates and suspending functions are
possible
▸ Non-blocking, UI is not frozen
LIBRARIES AND COROUTINES ON ANDROID
launch(UI) {
for (i in 10 downTo 1) {
hello.text = "Countdown $i ..."
delay(500)
}
hello.text = "Done!"
}
35. #droidconVN
OTHER UI THREAD CONCERNS
▸ For Jobs and cancelling coroutines, the same general principles still apply
▸ using launch {…} provides a reference to a Job
▸ can be cancelled with cancel() - for instance via UI control
▸ In UI scenarios useful to write own coroutines builders as extension functions
LIBRARIES AND COROUTINES ON ANDROID
button.onClick {
...
}
fun View.onClick(action: suspend () -> Unit) {
setOnClickListener {
launch(UI) {
action()
}
}
}
36. #droidconVN
OTHER UI THREAD CONCERNS
▸ Interesting topics for further study:
▸ Limit and manage coroutines via actors
▸ Dealing with event conflation
▸ Channel.CONFLATED
▸ Channel.UNLIMITED
LIBRARIES AND COROUTINES ON ANDROID
37. #droidconVN
THREAD BLOCKING OPERATIONS AND THE UI
▸ CPU-intensive computations and/or API calls
▸ Can’t be done from UI thread or UI thread-confined coroutine
▸ Solution: suspending functions with execution context CommonPool
LIBRARIES AND COROUTINES ON ANDROID
suspend fun fib(x: Int): Int = run(CommonPool) {
fibBlocking(x)
}
fun fibBlocking(x: Int): Int =
if (x <= 1) 1 else fibBlocking(x - 1) + fibBlocking(x - 2)
38. #droidconVN
NETWORK CALLS (I)
▸ Callback-free API call, handle offline-exceptions
LIBRARIES AND COROUTINES ON ANDROID
fun getUsers() : Deferred<List<Users>> {
return async(CommonPool) {
val request = Request.Builder().url(<SOMEURL>).build()
val response = OkHttpClient().newCall(request).execute()
// parse response...
}
}
39. #droidconVN
NETWORK CALLS (II)
▸ Handle exceptions in calling code
▸ Use result object’s await() to obtain the data (suspending the coroutine)
▸ Use launch {…} builder to trigger execution of getUsers
LIBRARIES AND COROUTINES ON ANDROID
launch(UI) {
try {
val result = getUsers()
adapter.setElements(result.await())
...
} catch (exception: IOException){
// we’re offline
}
40. #droidconVN
ANKO
▸ More often than not identified with declarative UI for Android/Kotlin
▸ But it also has APIs for:
▸ Async
▸ SQLite
▸ Anko 0.9 introduced naming changes around the Async API
▸ Since Anko 0.10, Anko has support for coroutines
LIBRARIES AND COROUTINES ON ANDROID
41. #droidconVN
LIBRARIES AND COROUTINES ON ANDROID
COROUTINES IN ANKO
▸ Add anko-coroutines dependency
▸ Earlier versions of Anko already had support for async handling
▸ New:
▸ Coroutines in listeners
▸ bg()
▸ asReference()
fun getData(): Data { ... }
fun showData(data: Data) { ... }
async(UI) {
val data: Deferred<Data> = bg {
// Runs on the background
getData()
}
// This code is executed on the UI thread
showData(data.await())
}
42. #droidconVN
LIBRARIES AND COROUTINES ON ANDROID
COROUTINES WITH ASYNCAWAIT
▸ Async/await approach
▸ Very rich library on top of Kotlin’s
coroutine core
▸ Plugins for Retrofit and rxJava
async {
val result = await {
//Long running code
}
// Use result
}
async {
val repos = await { github.getRepos() }
showList(repos)
repos.forEach { repo ->
val stats = await { github.getStats
(repo.name) }
showStats(repo, stats)
}
}
43. #droidconVN
LIBRARIES AND COROUTINES ON ANDROID
COROUTINES AND RXJAVA (I)
▸ There is an interop library for RX2 with coroutines: org.jetbrains.kotlinx:kotlinx-
coroutines-rx2
▸ rxJava -> coroutines: openSubscription(), awaitFirstOrDefault()
▸ coroutines -> rxJava:
▸ Job.asCompletable, Deferred.asSingle()
▸ Multiple coroutine builders: rxMaybe, rxCompletable etc.
44. #droidconVN
LIBRARIES AND COROUTINES ON ANDROID
COROUTINES AND RXJAVA (II)
▸ Method count:
▸ Coroutines ~10-20% less than rxJava
▸ APK size:
▸ Similar, ~10-20% smaller than with rxJava
▸ Interop libraries add quite a lot to method count and APK size.
46. #droidconVN
OTHER THINGS & FINAL THOUGHTS
UNIT TESTING SUSPENDING FUNCTIONS
▸ They need a coroutine to run, easiest way seems to be with runBlocking {…}
import kotlinx.coroutines.experimental.runBlocking
import org.testng.annotations.Test
class MyTest {
@Test
fun testMySuspendingFunction() = runBlocking<Unit> {
// your test code here
}
}
47. #droidconVN
OTHER THINGS & FINAL THOUGHTS
EXPERIMENTAL?
▸ Coroutines are currently experimental in Kotlin 1.x, however:
▸ Very sound approach of dealing with concurrency
▸ Jetbrains guarantees backwards compatibility
▸ Potentially no forward compatibility
▸ Coroutines can and should be used in production
▸ Quite low learning curve compared to rxJava and other libraries
48. #droidconVN
OTHER THINGS
RESOURCES
▸ Introduction to background processing in Android:
http://www.vogella.com/tutorials/AndroidBackgroundProcessing/article.html
▸ Coroutines design document:
https://github.com/Kotlin/kotlin-coroutines
▸ Coroutines guide:
https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md
▸ Java Futures & Coroutines:
https://blog.frankel.ch/concurrency-java-futures-kotlin-coroutines/#gsc.tab=0
▸ Anko:
https://github.com/Kotlin/anko
▸ AsyncAwait:
https://github.com/metalabdesign/AsyncAwait
▸ Coroutines and reactive streams:
https://github.com/Kotlin/kotlinx.coroutines/blob/master/reactive/README.md
49. #droidconVN
OTHER THINGS
GET IN TOUCH
Kai Koenig
Email: kai@ventego-creative.co.nz
Work: http://www.ventego-creative.co.nz
Twitter: @AgentK
Telegram: @kaikoenig
Slides: http://www.slideshare.com/agentk