Kotlin coroutines and Rx
Shaul Rosenzweig
Fullstack as a
service
- I am part of Tikal's Android group. We
meet, share, contribute and code
together on a monthly basis… We have
Tech radar, FuseDays, weekly lightning
talks, monthly meetups...
WHO AM I?
- Coding since 1980es
WHO AM I ?
Professional coding since late 1990es,
mobile since 2001, PalmOS, WinCE,
Symbian, Brew, J2ME, and Android
since 2009
Currently working in Cellebrite,
working on an application 100%
written in Kotlin, using Reactive,
Dagger, etc.
Why not switch to Kotlin?
We have always done it this way
James Gosling has a nicer beard than
Jake Wharton
Nobody died from boilerplate, you
spoilt brat!
WE ARE USED TO JAVA
OUR CODEBASE IS IN JAVA
PRODUCES SAME BYTECODE
PERSONAL PREFERENCE
PRO: For switching
LESS BOILERPLATE-> LESS BUGS
NULL SAFETY, etc
CAN BE ARGUED BOTH WAYS
KNOCKOUT BLOW: COROUTINES
FASTER EXECUTING CODE
SMALLER MEMORY FOOTPRINT
TAILOR MADE FOR REACTIVE
Regular synchronous query
fun query(): List<CallLogDao> {
val cursor = context.contentResolver.query(
CallLog.Calls.CONTENT_URI
null, null, null, null)
return queryAllLogs(cursor)
}
Reactive with SQL Brite
fun queryRxSqbrite(): Observable<List<CallLogDao>> {
return sqBriteResolver.createQuery(
CallLog.Calls.CONTENT_URI,
null, null, null, null, false)
.mapToList({
c -> CallLogDao(c)
}).firstElement().toObservable()
}
Consuming rx Observable
@Test
fun testRxSqBrite() {
val latch = CountDownLatch(1)
var gotList = ArrayList<CallLogDao> ()
manager.queryRxSqbrite().subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
list: List<CallLogDao> ->
gotList.addAll( list)
latch.countDown()
})
latch.await()
assertTrue("got some logs", gotList.size > 0)
}
What are coroutines?
Light weight way of asynchronously execute
computations that can be suspended without
blocking the thread
How are they different than
threads?
● Much lighter than threads
● Suspending is not blocking
● Millions of coroutines can run in one thread
● Less context switch overhead
● Less memory overhead
Trivial example
suspend fun doALongQuery() { … }
suspend fun processResults() { … }
suspend fun uploadResultsToServer() { … }
fun saveSessionStatus(status) { … }
suspend fun doALotOfWork() {
val queryResult = doALongQuery()
val processedResults = processResults(queryResult)
val status = uploadResultsToServer(processedResults)
saveSessionStatus(status)
}
Coroutine builder
fun doALotOfWork() {
launch(UI) {
val queryResult = doALongQuery()
val processedResults = processResults(queryResult)
val status = uploadResultsToServer(processedResults)
saveSessionStatus(status)
}
}
Async with Anko coroutines
fun queryAsyncAnko() : Deferred<List<CallLogDao>>? {
val cursor = context.contentResolver.query(
CallLog.Calls.CONTENT_URI,
null, null, null, null)
return bg {
queryAllLogs(cursor)
}
} else {
return null
}
}
Consuming Anko deferred
@Test
fun testAsyncAnko() {
val deferredLogs = manager.queryAsyncAnko()
assertNotNull("is it null", deferredLogs?.await())
assertTrue("got some?", actualLogs!!.size > 0)
}
How do coroutines work?
Not a new JVM feature, implemented via compile
time bytecode changes.
Code turned into a state machine
When compiling, suspension points are inserted
into the code.
When suspended, state and variables are saved
and serialized.
Suspended coroutine is called continuation.
IDE support
In Kotlin 1.2 they are opt-in
alpha feature
But ready for production
Async with Kotlin coroutines
suspend fun queryAsyncCoroutines(): Job? {
val cursor = context.contentResolver.query(
CallLog.Calls.CONTENT_URI,
null, null, null, null)
try {
job = launch(CommonPool) {
queryAllLogs(cursor, selection, selectionArgs)
}
} finally {
return job
}
}
What about Rx?
● Experimental lib, kotlinx coroutines reactive
● Wrappers for Reactive Streams, Reactor,
RxJava 1.x and RxJava 2.x using coroutines
● Under heavy development, and very
experimental
● At the time of writing, version is 0.18
Current status
IMPORTANT NOTE: We advise library authors to follow the
same convention: add the "experimental" (e.g.
com.example.experimental) suffix to your packages exposing
coroutine-based APIs so that your library remains binary
compatible. When the final API is released, follow these steps:
Rx2 example: flowable
return rxFlowable(coroutineContext) {
if (cursor.moveToFirst()) {
do {
send(CallLogDao(cursor))
} while (cursor.moveToNext())
}
}
Consuming rx2 Flowable
@Test
fun testRx2Coroutines() = runBlocking {
val source = manager.queryRxCoroutines(coroutineContext)
if (source != null) {
var success = false
source.observeOn(Schedulers.io(), false, 10)
.doOnComplete {
success = true
}
.subscribe {
log -> Log.d(TAG, "got " + log.toString())
} }
source.awaitLast()
assertTrue("got published via RX", success)
} else {
assertTrue("failed to generate RX stream", false)
}
}
But what is this coroutineContext?
Most important part is that they include a
coroutine dispatcher which determines on what
threads can the coroutine be executed.
They can be specified, or parent context can be
used.
Coroutine children
When a croutine is launched with context of
another coroutine, it becomes its child.
When a parent is cancelled, all of its children are
cancelled.
Waiting in coroutine context
If Thread.sleep(), CountdownLatch or similar
mechanism is used, it blocks the thread,
therefore coroutine does not get executed.
Instead use suspending, such as await, delay, or
similar.
We have seen the future
Not only for Android
Backend, Frontend, Mobile
and DevOps: Kotlin is fullstack
Links
● https://github.com/MaTriXy/mobileEvent-rx-kotlin
● https://github.com/Kotlin/kotlin-coroutines/
● https://github.com/Kotlin/kotlinx.coroutines/
● https://kotlin.github.io/kotlinx.coroutines/
● https://kotlinlang.org/docs/reference/coroutines.html
● https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html
● https://github.com/Kotlin/anko/wiki/Anko-Coroutines
● Talk by Andrey Breslav
● Talk by Roman Elizarov
● Talk by Svetlana Isakova
THANK YOU
THANK YOU
Shaul Rosenzweig
Email: shaulr@tikalk.com
Tel: +972-52-644-5240

Kotlin Coroutines and Rx

  • 1.
    Kotlin coroutines andRx Shaul Rosenzweig Fullstack as a service
  • 2.
    - I ampart of Tikal's Android group. We meet, share, contribute and code together on a monthly basis… We have Tech radar, FuseDays, weekly lightning talks, monthly meetups... WHO AM I?
  • 3.
    - Coding since1980es WHO AM I ?
  • 4.
    Professional coding sincelate 1990es, mobile since 2001, PalmOS, WinCE, Symbian, Brew, J2ME, and Android since 2009
  • 5.
    Currently working inCellebrite, working on an application 100% written in Kotlin, using Reactive, Dagger, etc.
  • 6.
    Why not switchto Kotlin?
  • 7.
    We have alwaysdone it this way
  • 8.
    James Gosling hasa nicer beard than Jake Wharton
  • 9.
    Nobody died fromboilerplate, you spoilt brat!
  • 10.
    WE ARE USEDTO JAVA
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
    CAN BE ARGUEDBOTH WAYS
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
    Regular synchronous query funquery(): List<CallLogDao> { val cursor = context.contentResolver.query( CallLog.Calls.CONTENT_URI null, null, null, null) return queryAllLogs(cursor) }
  • 23.
    Reactive with SQLBrite fun queryRxSqbrite(): Observable<List<CallLogDao>> { return sqBriteResolver.createQuery( CallLog.Calls.CONTENT_URI, null, null, null, null, false) .mapToList({ c -> CallLogDao(c) }).firstElement().toObservable() }
  • 24.
    Consuming rx Observable @Test funtestRxSqBrite() { val latch = CountDownLatch(1) var gotList = ArrayList<CallLogDao> () manager.queryRxSqbrite().subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ list: List<CallLogDao> -> gotList.addAll( list) latch.countDown() }) latch.await() assertTrue("got some logs", gotList.size > 0) }
  • 25.
    What are coroutines? Lightweight way of asynchronously execute computations that can be suspended without blocking the thread
  • 26.
    How are theydifferent than threads? ● Much lighter than threads ● Suspending is not blocking ● Millions of coroutines can run in one thread ● Less context switch overhead ● Less memory overhead
  • 27.
    Trivial example suspend fundoALongQuery() { … } suspend fun processResults() { … } suspend fun uploadResultsToServer() { … } fun saveSessionStatus(status) { … } suspend fun doALotOfWork() { val queryResult = doALongQuery() val processedResults = processResults(queryResult) val status = uploadResultsToServer(processedResults) saveSessionStatus(status) }
  • 28.
    Coroutine builder fun doALotOfWork(){ launch(UI) { val queryResult = doALongQuery() val processedResults = processResults(queryResult) val status = uploadResultsToServer(processedResults) saveSessionStatus(status) } }
  • 29.
    Async with Ankocoroutines fun queryAsyncAnko() : Deferred<List<CallLogDao>>? { val cursor = context.contentResolver.query( CallLog.Calls.CONTENT_URI, null, null, null, null) return bg { queryAllLogs(cursor) } } else { return null } }
  • 30.
    Consuming Anko deferred @Test funtestAsyncAnko() { val deferredLogs = manager.queryAsyncAnko() assertNotNull("is it null", deferredLogs?.await()) assertTrue("got some?", actualLogs!!.size > 0) }
  • 31.
    How do coroutineswork? Not a new JVM feature, implemented via compile time bytecode changes.
  • 32.
    Code turned intoa state machine When compiling, suspension points are inserted into the code. When suspended, state and variables are saved and serialized. Suspended coroutine is called continuation.
  • 33.
  • 34.
    In Kotlin 1.2they are opt-in alpha feature
  • 35.
    But ready forproduction
  • 36.
    Async with Kotlincoroutines suspend fun queryAsyncCoroutines(): Job? { val cursor = context.contentResolver.query( CallLog.Calls.CONTENT_URI, null, null, null, null) try { job = launch(CommonPool) { queryAllLogs(cursor, selection, selectionArgs) } } finally { return job } }
  • 37.
    What about Rx? ●Experimental lib, kotlinx coroutines reactive ● Wrappers for Reactive Streams, Reactor, RxJava 1.x and RxJava 2.x using coroutines ● Under heavy development, and very experimental ● At the time of writing, version is 0.18
  • 38.
    Current status IMPORTANT NOTE:We advise library authors to follow the same convention: add the "experimental" (e.g. com.example.experimental) suffix to your packages exposing coroutine-based APIs so that your library remains binary compatible. When the final API is released, follow these steps:
  • 39.
    Rx2 example: flowable returnrxFlowable(coroutineContext) { if (cursor.moveToFirst()) { do { send(CallLogDao(cursor)) } while (cursor.moveToNext()) } }
  • 40.
    Consuming rx2 Flowable @Test funtestRx2Coroutines() = runBlocking { val source = manager.queryRxCoroutines(coroutineContext) if (source != null) { var success = false source.observeOn(Schedulers.io(), false, 10) .doOnComplete { success = true } .subscribe { log -> Log.d(TAG, "got " + log.toString()) } } source.awaitLast() assertTrue("got published via RX", success) } else { assertTrue("failed to generate RX stream", false) } }
  • 41.
    But what isthis coroutineContext? Most important part is that they include a coroutine dispatcher which determines on what threads can the coroutine be executed. They can be specified, or parent context can be used.
  • 42.
    Coroutine children When acroutine is launched with context of another coroutine, it becomes its child. When a parent is cancelled, all of its children are cancelled.
  • 43.
    Waiting in coroutinecontext If Thread.sleep(), CountdownLatch or similar mechanism is used, it blocks the thread, therefore coroutine does not get executed. Instead use suspending, such as await, delay, or similar.
  • 44.
    We have seenthe future
  • 45.
    Not only forAndroid
  • 46.
    Backend, Frontend, Mobile andDevOps: Kotlin is fullstack
  • 47.
    Links ● https://github.com/MaTriXy/mobileEvent-rx-kotlin ● https://github.com/Kotlin/kotlin-coroutines/ ●https://github.com/Kotlin/kotlinx.coroutines/ ● https://kotlin.github.io/kotlinx.coroutines/ ● https://kotlinlang.org/docs/reference/coroutines.html ● https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html ● https://github.com/Kotlin/anko/wiki/Anko-Coroutines ● Talk by Andrey Breslav ● Talk by Roman Elizarov ● Talk by Svetlana Isakova
  • 48.
    THANK YOU THANK YOU ShaulRosenzweig Email: shaulr@tikalk.com Tel: +972-52-644-5240

Editor's Notes

  • #23 With or without callback Thread = 2mb RAM - 100 threads 10 000 threads Callback hell Error handling
  • #24 Rx - essentially future/promise
  • #25 No callback hell composable , propagates exceptions, but has combinators, different names diff libs
  • #28 Suspension points Same as blocking code Loops Can use high order functions like forEach no need for combinators
  • #29 Interface between regular and suspending world Returns immidatley
  • #30 Using async coroutine builder Returning deferred future
  • #34 Androidstudio 3 Marks suspension points
  • #40 Send will send on thread as specified in observeOn