Kotlin Coroutines and Android sitting in a tree

49
KOTLIN COROUTINES AND ANDROID SITTING IN A TREE... KAI KOENIG (@AGENTK)

Transcript of Kotlin Coroutines and Android sitting in a tree

Page 1: Kotlin Coroutines and Android sitting in a tree

KOTLIN COROUTINES AND ANDROID SITTING IN A TREE...

KAI KOENIG (@AGENTK)

Page 2: Kotlin Coroutines and Android sitting in a tree

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!

Twitter: @AgentK

Page 3: Kotlin Coroutines and Android sitting in a tree

AGENDA

AGENDA

▸ Kotlin in 5 minutes

▸ Why coroutines?

▸ Using coroutines in your Kotlin code

▸ Libraries and coroutines on Android

Page 4: Kotlin Coroutines and Android sitting in a tree

KOTLIN IN 5 MINUTES

https://www.flickr.com/photos/tschiae/8080742303/

Page 5: Kotlin Coroutines and Android sitting in a tree

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 5 MINUTES

Page 6: Kotlin Coroutines and Android sitting in a tree

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.51 (previously 1.1.5-1)

KOTLIN IN 5 MINUTES

Page 7: Kotlin Coroutines and Android sitting in a tree

HISTORY OF KOTLIN (III)

▸ At Google IO 2017, Kotlin became a first-grade language for Android

▸ Development of 1.2 is underway

▸ 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 5 MINUTES

Page 8: Kotlin Coroutines and Android sitting in a tree

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

Page 9: Kotlin Coroutines and Android sitting in a tree
Page 10: Kotlin Coroutines and Android sitting in a tree

THE 1.1 RELEASE

OVERVIEW

▸ JavaScript target is not experimental anymore (browser & NodeJS)

▸ Full Kotlin language support

▸ Large part of the stdlib is supported

▸ Coroutines (JVM-only, experimental)

▸ Lightweight concurrency mechanism

▸ Alternative to threads

▸ Tooling improvements & more language features

Page 11: Kotlin Coroutines and Android sitting in a tree

WHY COROUTINES?

https://www.flickr.com/photos/61299367@N05/9448165205

Page 12: Kotlin Coroutines and Android sitting in a tree

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)

Page 13: Kotlin Coroutines and Android sitting in a tree

http://slides.com/michaelholroyd/asyncnodejs/#/

Page 14: Kotlin Coroutines and Android sitting in a tree

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

▸ Go was one of the first modern languages re-introducing coroutines

Page 15: Kotlin Coroutines and Android sitting in a tree

WHY COROUTINES?

COROUTINES IN KOTLIN (I)

▸ Kotlin’s approach: Suspending functions

▸ Function/lambda that can be suspended and resumed

▸ 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

Page 16: Kotlin Coroutines and Android sitting in a tree

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

Page 17: Kotlin Coroutines and Android sitting in a tree

WHY COROUTINES?

COROUTINES IN KOTLIN (III)

▸ Multiple layers of libraries and integration points:

▸ Language support: suspending functions

▸ Low-level library in kotlinx.coroutines

▸ Mid-level library in kotlinx.coroutines

▸ High-level libraries (Anko etc.)

Page 18: Kotlin Coroutines and Android sitting in a tree

USING COROUTINES IN YOUR KOTLIN CODE

https://www.flickr.com/photos/97114911@N05/14720054418

Page 19: Kotlin Coroutines and Android sitting in a tree

USING COROUTINES IN YOUR KOTLIN CODE

FUNDAMENTALS

▸ Use Kotlin 1.1.4 as a minimum, better 1.1.51

▸ Enable experimental feature in Gradle

kotlin { experimental { coroutines 'enable' } }

compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.19.2'

Page 20: Kotlin Coroutines and Android sitting in a tree

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") }

Thread.sleep(10000) println("Stop on main thread") }

Page 21: Kotlin Coroutines and Android sitting in a tree

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") }

job.join() println("Stop on main thread") }

Page 22: Kotlin Coroutines and Android sitting in a tree

USING COROUTINES IN YOUR KOTLIN CODE

THREADS VS COROUTINES

import java.util.concurrent.atomic.AtomicInteger import kotlin.concurrent.thread

fun main(args: Array<String>) { val c = AtomicInteger()

for (i in 1..1_000_000) thread(start = true) { c.addAndGet(i) }

println(c.get()) }

Page 23: Kotlin Coroutines and Android sitting in a tree

import kotlinx.coroutines.experimental.* import java.util.concurrent.atomic.AtomicInteger

fun main(args: Array<String>) { val c = AtomicInteger()

for (i in 1..1_000_000) launch(CommonPool) { c.addAndGet(i) }

// Temporary hack to allow the coroutines to finish Thread.sleep(2000) println(c.get()) }

USING COROUTINES IN YOUR KOTLIN CODE

THREADS VS COROUTINES

Page 24: Kotlin Coroutines and Android sitting in a tree

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

Page 25: Kotlin Coroutines and Android sitting in a tree

USING COROUTINES IN YOUR KOTLIN CODE

ASYNC/AWAIT (II)

import kotlinx.coroutines.experimental.*

fun main(args: Array<String>) {

val deferred = (1..1_000_000).map { n -> async (CommonPool) { n } }

runBlocking { val sum = deferred.sumBy { it.await() } println("Sum: $sum") } }

Page 26: Kotlin Coroutines and Android sitting in a tree

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

Page 27: Kotlin Coroutines and Android sitting in a tree

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 }

Page 28: Kotlin Coroutines and Android sitting in a tree

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.

Page 29: Kotlin Coroutines and Android sitting in a tree

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 state machine

▸ launch, runBlocking, async etc. are often called coroutine builders

Page 30: Kotlin Coroutines and Android sitting in a tree

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

Page 31: Kotlin Coroutines and Android sitting in a tree

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.")

Page 32: Kotlin Coroutines and Android sitting in a tree

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) } }

Page 33: Kotlin Coroutines and Android sitting in a tree

USING COROUTINES IN YOUR KOTLIN CODE

THAT’S NOT ALL OF IT YET

▸ Coroutine context

▸ Coroutines and threads

▸ Channels

▸ Pipelines

▸ Dealing with state

▸ Shared (mutable) state & Actors

Compare Coroutines with Java Futures API

Page 34: Kotlin Coroutines and Android sitting in a tree

LIBRARIES AND COROUTINES ON ANDROID

https://www.flickr.com/photos/qiaomeng/4665885561/

Page 35: Kotlin Coroutines and Android sitting in a tree

MOTIVATION

▸ In general, UI-driven apps need to be aware of long-running processes

▸ In Android specifically, we can’t do any networking on the UI thread.

▸ 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.19.2'

Page 36: Kotlin Coroutines and Android sitting in a tree

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!" }

Page 37: Kotlin Coroutines and Android sitting in a tree

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() } } }

Page 38: Kotlin Coroutines and Android sitting in a tree

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

Page 39: Kotlin Coroutines and Android sitting in a tree

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)

Page 40: Kotlin Coroutines and Android sitting in a tree

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... } }

Page 41: Kotlin Coroutines and Android sitting in a tree

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 }

Page 42: Kotlin Coroutines and Android sitting in a tree

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

Page 43: Kotlin Coroutines and Android sitting in a tree

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()) }

Page 44: Kotlin Coroutines and Android sitting in a tree

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) } }

Page 45: Kotlin Coroutines and Android sitting in a tree

OTHER THINGS AND FINAL THOUGHTS

https://www.flickr.com/photos/chrispiascik/4054331891

Page 46: Kotlin Coroutines and Android sitting in a tree

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 } }

Page 47: Kotlin Coroutines and Android sitting in a tree

OTHER THINGS & FINAL THOUGHTS

EXPERIMENTAL?

▸ Coroutines are experimental in Kotlin 1.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

Page 48: Kotlin Coroutines and Android sitting in a tree

OTHER THINGS

RESOURCES

▸ 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

Page 49: Kotlin Coroutines and Android sitting in a tree

OTHER THINGS

GET IN TOUCH

Kai Koenig

Email: [email protected]

Work: http://www.ventego-creative.co.nz

Twitter: @AgentK

Telegram: @kaikoenig

Slides: http://www.slideshare.com/agentk