Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rx in Multiplatformland (Droidcon London 2019)

Rx in Multiplatformland (Droidcon London 2019)

Reactive programming has firmly become one of the modern Android developer's tools. More and more teams apply fully reactive architectures for their apps utilizing MV* approaches. This plays well for separating our OS-independent business logic from presentation layer. And while it can be implemented really nice using Kotlin, what if we want to expand this solid base further and don't repeat ourselves on every platform? What if we want to enter Multiplatformland?

Obviously, our best friend, RxJava, is not ready for that journey. But do we really have no alternatives? In this talk we'll discuss multiplatform friends of RxJava, such as Flow from Kotlin team and Reaktive from Badoo, their pros & cons, and how usable are they for our tasks.

Video: https://www.droidcon.com/media-detail?video=390487028

Sergey Ryabov

October 24, 2019
Tweet

More Decks by Sergey Ryabov

Other Decks in Programming

Transcript

  1. LAST YEAR ✦ "How to cook a well done MVI

    for Android" talk youtu.be/Ls0uKLqNFz4
  2. LAST YEAR ✦ "How to cook a well done MVI

    for Android" talk ✦ Platform-agnostic youtu.be/Ls0uKLqNFz4
  3. LAST YEAR ✦ "How to cook a well done MVI

    for Android" talk ✦ Platform-agnostic ✦ Based on Kotlin. MPP youtu.be/Ls0uKLqNFz4
  4. LAST YEAR ✦ "How to cook a well done MVI

    for Android" talk ✦ Platform-agnostic ✦ Based on Kotlin. MPP ✦ Based on RxJava. MPP youtu.be/Ls0uKLqNFz4
  5. LAST YEAR ✦ "How to cook a well done MVI

    for Android" talk ✦ Platform-agnostic ✦ Based on Kotlin. MPP ✦ Based on RxJava. MPP ✦ Maybe RxKotlin? youtu.be/Ls0uKLqNFz4
  6. LAST YEAR ✦ "How to cook a well done MVI

    for Android" talk ✦ Platform-agnostic ✦ Based on Kotlin. MPP ✦ Based on RxJava. MPP ✦ Maybe RxKotlin? youtu.be/Ls0uKLqNFz4 Real one
  7. THIS YEAR ✦ March – first public commits to the

    Reaktive repo ✦ April – Roman Elizarov writes an article about Flow Preview medium.com/@elizarov/cold-flows-hot-channels-d74769805f9
  8. FAST-FORWARD 6 MONTHS ✦ Reaktive – 1.0.1 ✦ Flow –

    stable since Coroutines 1.3.0 • Supported by SQLDelight 1.2.0
  9. FAST-FORWARD 6 MONTHS ✦ Reaktive – 1.0.1 ✦ Flow –

    stable since Coroutines 1.3.0 • Supported by SQLDelight 1.2.0 • Supported by Room 2.2.0
  10. Observable.just(1, 2, 3) .filter { it % 2 "== 0

    } .map { it * 10 } .subscribe { println(it) } RXJAVA2
  11. observableOf(1, 2, 3) .filter { it % 2 "== 0

    } .map { it * 10 } .subscribe { println(it) } REAKTIVE
  12. "flowOf(1, 2, 3) .filter { it % 2 "== 0

    } .map { it * 10 } .collect { println(it) } FLOW
  13. "flowOf(1, 2, 3) .filter { it % 2 "== 0

    } .map { it * 10 } .collect { println(it) } FLOW
  14. interface "Flow<out T> { suspend fun collect(collector: "FlowCollector<T>) } interface

    "FlowCollector<in T> { suspend fun emit(value: T) } FLOW
  15. fun <T, R> "Flow<T>.map(transform: suspend (value: T) "-> R): "Flow<R>

    = "flow { collect { value !-> emit(transform(value)) } } HOW DOES IT WORK? FLOW
  16. fun <T, R> "Flow<T>.map(transform: suspend (value: T) "-> R): "Flow<R>

    = "flow { this: "FlowCollector "-> [email protected] { value !-> this.emit(transform(value)) } } HOW DOES IT WORK? FLOW
  17. fun <T, R> "Flow<T>.map(transform: suspend (value: T) "-> R): "Flow<R>

    = "flow { this: "FlowCollector "-> [email protected] { value !-> this.emit(transform(value)) } } HOW DOES IT WORK? FLOW
  18. fun <T, R> "Flow<T>.map(transform: suspend (value: T) "-> R): "Flow<R>

    = "flow { this: "FlowCollector "-> [email protected] { value !-> this.emit(transform(value)) } } HOW DOES IT WORK? FLOW
  19. fun <T, R> Observable<T>.map(mapper: (T) "-> R): Observable<R> = observable

    { emitter !-> subscribeSafe( object : ObservableObserver<T>, CompletableCallbacks by emitter { override fun onSubscribe(disposable: Disposable) { emitter.setDisposable(disposable) } override fun onNext(value: T) { emitter.tryCatch(block = { mapper(value) }, onSuccess = emitter"::onNext) } } ) } HOW DOES IT WORK? REAKTIVE
  20. fun <T, R> Observable<T>.map(mapper: (T) "-> R): Observable<R> = observable

    { emitter !-> subscribeSafe( object : ObservableObserver<T>, CompletableCallbacks by emitter { override fun onSubscribe(disposable: Disposable) { emitter.setDisposable(disposable) } override fun onNext(value: T) { emitter.tryCatch(block = { mapper(value) }, onSuccess = emitter"::onNext) } } ) } HOW DOES IT WORK? REAKTIVE
  21. fun <T, R> Observable<T>.map(mapper: (T) "-> R): Observable<R> = observable

    { emitter !-> subscribeSafe( object : ObservableObserver<T>, CompletableCallbacks by emitter { override fun onSubscribe(disposable: Disposable) { emitter.setDisposable(disposable) } override fun onNext(value: T) { emitter.tryCatch(block = { mapper(value) }, onSuccess = emitter"::onNext) } } ) } HOW DOES IT WORK? REAKTIVE
  22. fun <T, R> Observable<T>.map(mapper: (T) "-> R): Observable<R> = observable

    { emitter !-> subscribeSafe( object : ObservableObserver<T>, CompletableCallbacks by emitter { override fun onSubscribe(disposable: Disposable) { emitter.setDisposable(disposable) }. override fun onNext(value: T) { emitter.tryCatch(block = { mapper(value) }, onSuccess = emitter"::onNext) } } ) } HOW DOES IT WORK? REAKTIVE
  23. fun <T, R> Observable<T>.map(mapper: (T) "-> R): Observable<R> = observable

    { emitter !-> subscribeSafe( object : ObservableObserver<T>, CompletableCallbacks by emitter { override fun onSubscribe(disposable: Disposable) { emitter.setDisposable(disposable) } override fun onNext(value: T) { emitter.tryCatch(block = { mapper(value) }, onSuccess = emitter"::onNext) } } ) } HOW DOES IT WORK? REAKTIVE
  24. public final class ObservableMap<T, U> extends AbstractObservableWithUpstream<T, U> { final

    Function<? super T, ? extends U> function; public ObservableMap(ObservableSource<T> source, Function<? super T, ? extends U> function) { super(source); this.function = function; } @Override public void subscribeActual(Observer<? super U> t) { source.subscribe(new MapObserver<T, U>(t, function)); } static final class MapObserver<T, U> extends BasicFuseableObserver<T, U> { final Function<? super T, ? extends U> mapper; MapObserver(Observer<? super U> actual, Function<? super T, ? extends U> mapper) { super(actual); this.mapper = mapper; } @Override public void onNext(T t) { if (done) { return; } if (sourceMode "!= NONE) { downstream.onNext(null); return; } U v; try { v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value."); } catch (Throwable ex) { fail(ex); return; } downstream.onNext(v); } @Override public int requestFusion(int mode) { return transitiveBoundaryFusion(mode); } @Nullable @Override public U poll() throws Exception { T t = qd.poll(); return t "!= null ? ObjectHelper.<U>requireNonNull(mapper.apply(t), "The mapper function returned a null value.") : null; } } } HOW DOES IT WORK? RXJA... OH, DAMN
  25. public final class ObservableMap<T, U> extends AbstractObservableWithUpstream<T, U> { final

    Function<? super T, ? extends U> function; public ObservableMap(ObservableSource<T> source, Function<? super T, ? extends U> function) { super(source); this.function = function; } @Override public void subscribeActual(Observer<? super U> t) { source.subscribe(new MapObserver<T, U>(t, function)); } static final class MapObserver<T, U> extends BasicFuseableObserver<T, U> { final Function<? super T, ? extends U> mapper; MapObserver(Observer<? super U> actual, Function<? super T, ? extends U> mapper) { super(actual); this.mapper = mapper; } @Override public void onNext(T t) { if (done) { return; } if (sourceMode "!= NONE) { downstream.onNext(null); return; } U v; try { v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value."); } catch (Throwable ex) { fail(ex); return; } downstream.onNext(v); } @Override public int requestFusion(int mode) { return transitiveBoundaryFusion(mode); } @Nullable @Override public U poll() throws Exception { T t = qd.poll(); return t "!= null ? ObjectHelper.<U>requireNonNull(mapper.apply(t), "The mapper function returned a null value.") : null; } } } HOW DOES IT WORK? RXJA... OH, DAMN
  26. API

  27. OPERATORS RxJava2/Reaktive Flow flatMap flatMapMerge concatMap flatMapConcat switchMap flatMapLatest flatten

    flattenConcat merge flattenMerge combineLatest combine skip drop onError* catch ... ...
  28. MISSING IN FLOW ✦ groupBy ✦ publish ✦ share ✦

    replay ✦ withLatestFrom ✦ takeUntil ✦ parallel
  29. MISSING IN FLOW ✦ groupBy ✦ publish ✦ share ✦

    replay ✦ withLatestFrom ✦ takeUntil ✦ parallel github.com/Kotlin/ kotlinx.coroutines/issues/1261
  30. MISSING IN FLOW ✦ groupBy ✦ publish ✦ share ✦

    replay ✦ withLatestFrom ✦ takeUntil ✦ parallel David Karnok's github.com/akarnokd/kotlin- "flow-extensions
  31. MISSING IN FLOW ✦ groupBy ✦ publish ✦ share ✦

    replay ✦ withLatestFrom ✦ takeUntil ✦ parallel David Karnok's github.com/akarnokd/kotlin- "flow-extensions Only JVM
  32. observableOf(1, 2, 3) .filter { it % 2 "== 0

    } .subscribeOn(ioScheduler) .observeOn(computationScheduler) .map { it * 10 } .observeOn(mainScheduler) .subscribe { println(it) } THREADING. REAKTIVE
  33. observableOf(1, 2, 3) .filter { it % 2 "== 0

    } .subscribeOn(ioScheduler) .observeOn(computationScheduler) .map { it * 10 } .observeOn(mainScheduler) .subscribe { println(it) } THREADING. REAKTIVE
  34. observableOf(1, 2, 3) .filter { it % 2 "== 0

    } .subscribeOn(ioScheduler) .observeOn(computationScheduler) .map { it * 10 } .observeOn(mainScheduler) .subscribe { println(it) } THREADING. REAKTIVE
  35. observableOf(1, 2, 3) .filter { it % 2 "== 0

    } .subscribeOn(ioScheduler) .observeOn(computationScheduler) .map { it * 10 } .observeOn(mainScheduler) .subscribe { println(it) } THREADING. REAKTIVE
  36. observableOf(1, 2, 3) .filter { it % 2 "== 0

    } .subscribeOn(ioScheduler) .observeOn(computationScheduler) .map { it * 10 } .observeOn(mainScheduler) .subscribe { println(it) } THREADING. REAKTIVE
  37. observableOf(1, 2, 3) .filter { it % 2 "== 0

    } .subscribeOn(ioScheduler) .observeOn(computationScheduler) .map { it * 10 } .observeOn(mainScheduler) .subscribe { println(it) } THREADING. REAKTIVE
  38. observableOf(1, 2, 3) .filter { it % 2 "== 0

    } .subscribeOn(ioScheduler) .observeOn(computationScheduler) .map { it * 10 } .observeOn(mainScheduler) .subscribe { println(it) } THREADING. REAKTIVE
  39. observableOf(1, 2, 3) .filter { it % 2 "== 0

    } .subscribeOn(ioScheduler) .observeOn(computationScheduler) .map { it * 10 } .observeOn(mainScheduler) .subscribe { println(it) } THREADING. REAKTIVE
  40. observableOf(1, 2, 3) .filter { it % 2 "== 0

    } .subscribeOn(ioScheduler) .observeOn(computationScheduler) .map { it * 10 } .observeOn(mainScheduler) .subscribe { println(it) } THREADING. REAKTIVE
  41. observableOf(1, 2, 3) .filter { it % 2 "== 0

    } .subscribeOn(ioScheduler) .observeOn(computationScheduler) .map { it * 10 } .observeOn(mainScheduler) .subscribe { println(it) } THREADING. REAKTIVE
  42. "flowOf(1, 2, 3) .filter { it % 2 "== 0

    } ."flowOn(Dispatchers.IO) .map { it * 10 } ."flowOn(Dispatchers.Default) .collect { println(it) } THREADING. FLOW
  43. scope.launch(Dispatchers.Main) { "flowOf(1, 2, 3) .filter { it % 2

    "== 0 } ."flowOn(Dispatchers.IO) .map { it * 10 } ."flowOn(Dispatchers.Default) .collect { println(it) } } THREADING. FLOW
  44. scope.launch(Dispatchers.Main) { "flowOf(1, 2, 3) .filter { it % 2

    "== 0 } ."flowOn(Dispatchers.IO) .map { it * 10 } ."flowOn(Dispatchers.Default) .collect { println(it) } } THREADING. FLOW
  45. scope.launch(Dispatchers.Main) { "flowOf(1, 2, 3) .filter { it % 2

    "== 0 } ."flowOn(Dispatchers.IO) .map { it * 10 } ."flowOn(Dispatchers.Default) .collect { println(it) } } THREADING. FLOW
  46. scope.launch(Dispatchers.Main) { "flowOf(1, 2, 3) .filter { it % 2

    "== 0 } ."flowOn(Dispatchers.IO) .map { it * 10 } ."flowOn(Dispatchers.Default) .collect { println(it) } } THREADING. FLOW
  47. scope.launch(Dispatchers.Main) { "flowOf(1, 2, 3) .filter { it % 2

    "== 0 } ."flowOn(Dispatchers.IO) .map { it * 10 } ."flowOn(Dispatchers.Default) .collect { println(it) } } THREADING. FLOW
  48. scope.launch(Dispatchers.Main) { "flowOf(1, 2, 3) .filter { it % 2

    "== 0 } ."flowOn(Dispatchers.IO) .map { it * 10 } ."flowOn(Dispatchers.Default) .collect { println(it) } } THREADING. FLOW
  49. scope.launch(Dispatchers.Main) { "flowOf(1, 2, 3) .filter { it % 2

    "== 0 } ."flowOn(Dispatchers.IO) .map { it * 10 } ."flowOn(Dispatchers.Default) .collect { println(it) } } THREADING. FLOW
  50. scope.launch(Dispatchers.Main) { "flowOf(1, 2, 3) .filter { it % 2

    "== 0 } ."flowOn(Dispatchers.IO) .map { it * 10 } ."flowOn(Dispatchers.Default) .collect { println(it) } } THREADING. FLOW
  51. scope.launch(Dispatchers.Main) { "flowOf(1, 2, 3) .filter { it % 2

    "== 0 } ."flowOn(Dispatchers.IO) .map { it * 10 } ."flowOn(Dispatchers.Default) .collect { println(it) } } THREADING. FLOW
  52. scope.launch(Dispatchers.Main) { "flowOf(1, 2, 3) .filter { it % 2

    "== 0 } ."flowOn(Dispatchers.IO) .map { it * 10 } ."flowOn(Dispatchers.Default) .collect { println(it) } } THREADING. FLOW
  53. scope.launch(Dispatchers.Main) { "flowOf(1, 2, 3) .filter { it % 2

    "== 0 } ."flowOn(Dispatchers.IO) .map { it * 10 } ."flowOn(Dispatchers.Default) .collect { println(it) } } THREADING. FLOW
  54. SCHEDULERS RxJava2 Reaktive Flow AndroidSchedulers.mainThread() mainScheduler Dispatchers.Main Schedulers.io() ioScheduler Dispatchers.IO

    Schedulers.computation() computationScheduler Dispatchers.Default Schedulers.trampoline() trampolineScheduler Executor.asCoroutineDispatcher() Schedulers.newThread() i.r.Scheduler.asReaktive() Executor.asCoroutineDispatcher() Schedulers.single() i.r.Scheduler.asReaktive() Executor.asCoroutineDispatcher()
  55. SCHEDULERS RxJava2 Reaktive Flow AndroidSchedulers.mainThread() mainScheduler Dispatchers.Main Schedulers.io() ioScheduler Dispatchers.IO

    Schedulers.computation() computationScheduler Dispatchers.Default Schedulers.trampoline() trampolineScheduler Executor.asCoroutineDispatcher() Schedulers.newThread() i.r.Scheduler.asReaktive() Executor.asCoroutineDispatcher() Schedulers.single() i.r.Scheduler.asReaktive() Executor.asCoroutineDispatcher()
  56. SCHEDULERS RxJava2 Reaktive Flow AndroidSchedulers.mainThread() mainScheduler Dispatchers.Main Schedulers.io() ioScheduler Dispatchers.IO

    Schedulers.computation() computationScheduler Dispatchers.Default Schedulers.trampoline() trampolineScheduler Executor.asCoroutineDispatcher() Schedulers.newThread() i.r.Scheduler.asReaktive() Executor.asCoroutineDispatcher() Schedulers.single() i.r.Scheduler.asReaktive() Executor.asCoroutineDispatcher() Only JVM
  57. SUBJECTS ✦ RxJava2: Behavior, Publish, Replay ✦ Reaktive: Behavior, Publish

    ✦ Flow: Behavior (Con!flatedBroadcastChannel) DataFlow Proposal: github.com/Kotlin/kotlinx.coroutines/pull/1354
  58. SUBJECTS ✦ RxJava2: Behavior, Publish, Replay ✦ Reaktive: Behavior, Publish

    ✦ Flow: Behavior (Con!flatedBroadcastChannel) ✦ Flow + Karnok: Behavior, Publish, Replay DataFlow Proposal: github.com/Kotlin/kotlinx.coroutines/pull/1354
  59. SUBJECTS ✦ RxJava2: Behavior, Publish, Replay ✦ Reaktive: Behavior, Publish

    ✦ Flow: Behavior (Con!flatedBroadcastChannel) ✦ Flow + Karnok: Behavior, Publish, Replay Only JVM DataFlow Proposal: github.com/Kotlin/kotlinx.coroutines/pull/1354
  60. INTEROP & GRADUAL ADOPTION 1. Connect components. Adapter artifacts: •

    org.jetbrains.kotlinx:kotlinx-coroutines-rx2 • com.badoo.reaktive:coroutines-interop
  61. INTEROP & GRADUAL ADOPTION 1. Connect components. Adapter artifacts: •

    org.jetbrains.kotlinx:kotlinx-coroutines-rx2 • com.badoo.reaktive:coroutines-interop • com.badoo.reaktive:rxjava2-interop
  62. INTEROP & GRADUAL ADOPTION 2. Convert components. Check this out:

    kotlinx-coroutines-core/common/src/!flow/Migration.kt
  63. INTEROP & GRADUAL ADOPTION 2. Convert components. Check this out:

    kotlinx-coroutines-core/common/src/!flow/Migration.kt @Deprecated( level = DeprecationLevel.ERROR, message = "!Flow analogue of '!flatten' is '!flattenConcat'", replaceWith = ReplaceWith("!flattenConcat()") ) fun <T> "Flow<"Flow<T">>."flatten(): "Flow<T> = noImpl() github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/common/src/flow/Migration.kt
  64. dataObservable() ."flatMap { crashMap(it) } .subscribe( onNext = { handle(it)

    }, onError = { report(it) } ) ERROR HANDLING BITS
  65. dataObservable() ."flatMap { crashMap(it) } .subscribe( onNext = { handle(it)

    }, onError = { report(it) } ) ERROR HANDLING BITS ❓
  66. dataObservable() ."flatMap { map(it) } .subscribe( onNext = { crashHandle(it)

    }, onError = { report(it) } ) ERROR HANDLING BITS
  67. data"Flow() ."flatMap { map(it) } .subscribe( onNext = { crashHandle(it)

    }, onError = { report(it) } ) ERROR HANDLING BITS. FLOW
  68. data"Flow() ."flatMapMerge { map(it) } .subscribe( onNext = { crashHandle(it)

    }, onError = { report(it) } ) ERROR HANDLING BITS. FLOW
  69. data"Flow() ."flatMapMerge { map(it) } .catch { report(it) } .subscribe(

    onNext = { crashHandle(it) } ) ERROR HANDLING BITS. FLOW
  70. data"Flow() ."flatMapMerge { map(it) } .catch { report(it) } .collect

    { crashHandle(it) } ERROR HANDLING BITS. FLOW
  71. data"Flow() ."flatMapMerge { map(it) } .catch { report(it) } .collect

    { crashHandle(it) } ERROR HANDLING BITS. FLOW ❓
  72. data"Flow() ."flatMapMerge { map(it) } .catch { report(it) } .collect

    { crashHandle(it) } ERROR HANDLING BITS. FLOW ❓ We crash!
  73. data"Flow() ."flatMapMerge { map(it) } .onEach { crashHandle(it) } .catch

    { report(it) } .collect() ERROR HANDLING BITS. FLOW
  74. data"Flow() ."flatMapMerge { map(it) } .onEach { crashHandle(it) } .catch

    { report(it) } .collect() ERROR HANDLING BITS. FLOW Last in the chain
  75. data"Flow() ."flatMapMerge { map(it) } .onEach { crashHandle(it) } .catch

    { report(it) } .collect() ERROR HANDLING BITS. FLOW Last in the chain github.com/MartinDevi/Coroutines-Completion-Demo Pay attention!
  76. KOTLIN NATIVE. REAKTIVE ✦ Workers-based threading ✦ Native freeze for

    switching threads, i.e. almost always ✦ threadLocal operator to stop freezing
  77. KOTLIN NATIVE. REAKTIVE ✦ Workers-based threading ✦ Native freeze for

    switching threads, i.e. almost always ✦ threadLocal operator to stop freezing ✦ In practice, not only immutable events, but also captured vars in callbacks
  78. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 156.474 ms/op RxJava2ObservablePlaysScrabble avgt

    90.505 ms/op RxJava2FlowablePlaysScrabble avgt 96.144 ms/op FlowPlaysScrabble avgt 84.520 ms/op ReaktivePlaysScrabble avgt 208.666 ms/op THROUGHPUT github.com/akarnokd/akarnokd-misc MacBook Pro 2018, 2.9 GHz, Intel Core i9
  79. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 156.474 ms/op RxJava2ObservablePlaysScrabble avgt

    90.505 ms/op RxJava2FlowablePlaysScrabble avgt 96.144 ms/op FlowPlaysScrabble avgt 84.520 ms/op ReaktivePlaysScrabble avgt 208.666 ms/op THROUGHPUT github.com/akarnokd/akarnokd-misc MacBook Pro 2018, 2.9 GHz, Intel Core i9
  80. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 156.474 ms/op RxJava2ObservablePlaysScrabble avgt

    90.505 ms/op RxJava2FlowablePlaysScrabble avgt 96.144 ms/op FlowPlaysScrabble avgt 84.520 ms/op ReaktivePlaysScrabble avgt 208.666 ms/op THROUGHPUT github.com/akarnokd/akarnokd-misc MacBook Pro 2018, 2.9 GHz, Intel Core i9
  81. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 156.474 ms/op RxJava2ObservablePlaysScrabble avgt

    90.505 ms/op RxJava2FlowablePlaysScrabble avgt 96.144 ms/op FlowPlaysScrabble avgt 84.520 ms/op ReaktivePlaysScrabble avgt 208.666 ms/op THROUGHPUT github.com/akarnokd/akarnokd-misc MacBook Pro 2018, 2.9 GHz, Intel Core i9
  82. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 156.474 ms/op RxJava2ObservablePlaysScrabble avgt

    90.505 ms/op RxJava2FlowablePlaysScrabble avgt 96.144 ms/op FlowPlaysScrabble avgt 84.520 ms/op FlowPlaysScrabbleFlatMap avgt 744.464 ms/op ReaktivePlaysScrabble avgt 208.666 ms/op THROUGHPUT github.com/akarnokd/akarnokd-misc MacBook Pro 2018, 2.9 GHz, Intel Core i9
  83. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 156.474 ms/op RxJava2ObservablePlaysScrabble avgt

    90.505 ms/op RxJava2FlowablePlaysScrabble avgt 96.144 ms/op FlowPlaysScrabble avgt 84.520 ms/op FlowPlaysScrabbleFlatMap avgt 744.464 ms/op ReaktivePlaysScrabble avgt 208.666 ms/op THROUGHPUT github.com/akarnokd/akarnokd-misc MacBook Pro 2018, 2.9 GHz, Intel Core i9
  84. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 254.334 MB/op RxJava2ObservablePlaysScrabble avgt

    123.316 MB/op RxJava2FlowablePlaysScrabble avgt 139.861 MB/op FlowPlaysScrabble avgt 247.987 MB/op FlowPlaysScrabbleFlatMap avgt 780.728 MB/op ReaktivePlaysScrabble avgt 351.192 MB/op MEMORY
  85. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 254.334 MB/op RxJava2ObservablePlaysScrabble avgt

    123.316 MB/op RxJava2FlowablePlaysScrabble avgt 139.861 MB/op FlowPlaysScrabble avgt 247.987 MB/op FlowPlaysScrabbleFlatMap avgt 780.728 MB/op ReaktivePlaysScrabble avgt 351.192 MB/op MEMORY
  86. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 254.334 MB/op RxJava2ObservablePlaysScrabble avgt

    123.316 MB/op RxJava2FlowablePlaysScrabble avgt 139.861 MB/op FlowPlaysScrabble avgt 247.987 MB/op FlowPlaysScrabbleFlatMap avgt 780.728 MB/op ReaktivePlaysScrabble avgt 351.192 MB/op MEMORY
  87. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 254.334 MB/op RxJava2ObservablePlaysScrabble avgt

    123.316 MB/op RxJava2FlowablePlaysScrabble avgt 139.861 MB/op FlowPlaysScrabble avgt 247.987 MB/op FlowPlaysScrabbleFlatMap avgt 780.728 MB/op ReaktivePlaysScrabble avgt 351.192 MB/op MEMORY
  88. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 254.334 MB/op RxJava2ObservablePlaysScrabble avgt

    123.316 MB/op RxJava2FlowablePlaysScrabble avgt 139.861 MB/op FlowPlaysScrabble avgt 247.987 MB/op FlowPlaysScrabbleFlatMap avgt 780.728 MB/op ReaktivePlaysScrabble avgt 351.192 MB/op MEMORY
  89. CONCLUSIONS Flow ✦ Different mental model ✦ Native? Not yet

    ready for MPP ✦ Not all operators we are used to are available
  90. CONCLUSIONS Flow ✦ Different mental model ✦ Native? Not yet

    ready for MPP ✦ Not all operators we are used to are available ✦ Official support from JetBrains & Google
  91. CONCLUSIONS Flow ✦ Different mental model ✦ Native? Not yet

    ready for MPP ✦ Not all operators we are used to are available ✦ Official support from JetBrains & Google Reaktive ✦ Familiar API
  92. CONCLUSIONS Flow ✦ Different mental model ✦ Native? Not yet

    ready for MPP ✦ Not all operators we are used to are available ✦ Official support from JetBrains & Google Reaktive ✦ Familiar API ✦ Real-ish Native support. MPP
  93. CONCLUSIONS Flow ✦ Different mental model ✦ Native? Not yet

    ready for MPP ✦ Not all operators we are used to are available ✦ Official support from JetBrains & Google Reaktive ✦ Familiar API ✦ Real-ish Native support. MPP ✦ No backpressure
  94. CONCLUSIONS Flow ✦ Different mental model ✦ Native? Not yet

    ready for MPP ✦ Not all operators we are used to are available ✦ Official support from JetBrains & Google Reaktive ✦ Familiar API ✦ Real-ish Native support. MPP ✦ No backpressure ✦ 1.0.x, but not really production ready
  95. CONCLUSIONS Flow ✦ Different mental model ✦ Native? Not yet

    ready for MPP ✦ Not all operators we are used to are available ✦ Official support from JetBrains & Google Reaktive ✦ Familiar API ✦ Real-ish Native support. MPP ✦ No backpressure ✦ 1.0.x, but not really production ready ✦ Native impl affects performance on other platforms (JVM) - is being fixed
  96. CONCLUSIONS Flow ✦ Different mental model ✦ Native? Not yet

    ready for MPP ✦ Not all operators we are used to are available ✦ Official support from JetBrains & Google Reaktive ✦ Familiar API ✦ Real-ish Native support. MPP ✦ No backpressure ✦ 1.0.x, but not really production ready ✦ Native impl affects performance on other platforms (JVM) - is being fixed ✦ Very responsive maintainers
  97. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 156.474 ms/op RxJava2ObservablePlaysScrabble avgt

    90.505 ms/op RxJava2FlowablePlaysScrabble avgt 96.144 ms/op FlowPlaysScrabble avgt 84.520 ms/op FlowPlaysScrabbleFlatMap avgt 744.464 ms/op ReaktivePlaysScrabble avgt 208.666 ms/op Reaktive102PlaysScrabble avgt 126.115 ms/op THROUGHPUT
  98. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 156.474 ms/op RxJava2ObservablePlaysScrabble avgt

    90.505 ms/op RxJava2FlowablePlaysScrabble avgt 96.144 ms/op FlowPlaysScrabble avgt 84.520 ms/op FlowPlaysScrabbleFlatMap avgt 744.464 ms/op ReaktivePlaysScrabble avgt 208.666 ms/op Reaktive102PlaysScrabble avgt 106.115 ms/op THROUGHPUT
  99. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 156.474 ms/op RxJava2ObservablePlaysScrabble avgt

    90.505 ms/op RxJava2FlowablePlaysScrabble avgt 96.144 ms/op FlowPlaysScrabble avgt 84.520 ms/op FlowPlaysScrabbleFlatMap avgt 744.464 ms/op ReaktivePlaysScrabble avgt 208.666 ms/op Reaktive102PlaysScrabble avgt 106.115 ms/op THROUGHPUT 1.0.2 *
  100. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 254.334 MB/op RxJava2ObservablePlaysScrabble avgt

    123.316 MB/op RxJava2FlowablePlaysScrabble avgt 139.861 MB/op FlowPlaysScrabble avgt 247.987 MB/op FlowPlaysScrabbleFlatMap avgt 780.728 MB/op ReaktivePlaysScrabble avgt 351.192 MB/op Reaktive102PlaysScrabble avgt 177.169 MB/op MEMORY 1.0.2
  101. Benchmark Mode Score Units RxJava1ObservablePlaysScrabble avgt 254.334 MB/op RxJava2ObservablePlaysScrabble avgt

    123.316 MB/op RxJava2FlowablePlaysScrabble avgt 139.861 MB/op FlowPlaysScrabble avgt 247.987 MB/op FlowPlaysScrabbleFlatMap avgt 780.728 MB/op ReaktivePlaysScrabble avgt 351.192 MB/op Reaktive102PlaysScrabble avgt 177.169 MB/op MEMORY 1.0.2
  102. LINKS ✦ Reaktive repo github.com/badoo/Reaktive ✦ Flow docs kotlinlang.org/docs/reference/coroutines/flow.html ✦

    Reactive Scrabble Benchmark akarnokd.blogspot.com/2016/12/the-reactive- scrabble-benchmarks.html ✦ Job Hierarchy ambiguity proandroiddev.com/kotlin-coroutine-job-hierarchy-finish- cancel-and-fail-2d3d42a768a9 ✦ Sample code github.com/colriot/talk-rx-multiplatform ✦ Cover photo unsplash.com/photos/PqU63ujaIa4