The document provides an overview of Kotlin and its coroutines, detailing Kotlin's history, features, and implementation in Android development. It emphasizes the advantages of using coroutines for asynchronous programming, including lighter resource management and non-blocking operations, alongside practical examples. Additionally, it discusses libraries that support coroutines in Android and touches on testing and experimental status of coroutines in Kotlin.
HELLO HELLO My name isKai! 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
3.
AGENDA AGENDA ▸ Kotlin in5 minutes ▸ Why coroutines? ▸ Using coroutines in your Kotlin code ▸ Libraries and coroutines on Android
4.
KOTLIN IN 5MINUTES https://www.flickr.com/photos/tschiae/8080742303/
5.
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
6.
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
7.
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
8.
KOTLIN IN 5MINUTES 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.
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
WHY COROUTINES? MOTIVATION ▸ Asynchronousprogramming 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)
WHY COROUTINES? HISTORY ▸ Coroutineswere 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
15.
WHY COROUTINES? COROUTINES INKOTLIN (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
16.
WHY COROUTINES? COROUTINES INKOTLIN (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
17.
WHY COROUTINES? COROUTINES INKOTLIN (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.)
USING COROUTINES INYOUR 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'
20.
USING COROUTINES INYOUR 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") }
21.
USING COROUTINES INYOUR 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") }
22.
USING COROUTINES INYOUR 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()) }
USING COROUTINES INYOUR 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
25.
USING COROUTINES INYOUR 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") } }
26.
USING COROUTINES INYOUR 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
27.
USING COROUTINES INYOUR 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 }
28.
USING COROUTINES INYOUR 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.
29.
USING COROUTINES INYOUR 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
30.
USING COROUTINES INYOUR 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
31.
USING COROUTINES INYOUR 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.")
32.
USING COROUTINES INYOUR 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) } }
33.
USING COROUTINES INYOUR 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
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'
36.
UI CONTEXT ▸ TheAndroid 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!" }
37.
OTHER UI THREADCONCERNS ▸ 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() } } }
38.
OTHER UI THREADCONCERNS ▸ Interesting topics for further study: ▸ Limit and manage coroutines via actors ▸ Dealing with event conflation ▸ Channel.CONFLATED ▸ Channel.UNLIMITED LIBRARIES AND COROUTINES ON ANDROID
39.
THREAD BLOCKING OPERATIONSAND 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)
40.
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... } }
41.
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 }
42.
ANKO ▸ More oftenthan 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
43.
LIBRARIES AND COROUTINESON 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()) }
44.
LIBRARIES AND COROUTINESON 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) } }
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.
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
OTHER THINGS GET INTOUCH 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