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

C50a1f407bc251b7395c0984be4327e9?s=128

Sergey Ryabov

October 24, 2019
Tweet

More Decks by Sergey Ryabov

Other Decks in Programming

Transcript

  1. RX IN MULTIPLATFORMLAND Sergey Ryabov Facebook

  2. LAST YEAR

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

    for Android" talk
  4. LAST YEAR ✦ "How to cook a well done MVI

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

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

    for Android" talk ✦ Platform-agnostic ✦ Based on Kotlin. MPP youtu.be/Ls0uKLqNFz4
  7. 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
  8. 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
  9. 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
  10. THIS YEAR

  11. THIS YEAR ✦ March – first public commits to the

    Reaktive repo
  12. 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
  13. FAST-FORWARD 6 MONTHS

  14. FAST-FORWARD 6 MONTHS ✦ Reaktive – 1.0.1

  15. FAST-FORWARD 6 MONTHS ✦ Reaktive – 1.0.1 ✦ Flow –

    stable since Coroutines 1.3.0
  16. FAST-FORWARD 6 MONTHS ✦ Reaktive – 1.0.1 ✦ Flow –

    stable since Coroutines 1.3.0 • Supported by SQLDelight 1.2.0
  17. 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
  18. Observable.just(1, 2, 3) .filter { it % 2 "== 0

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

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

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

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

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

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

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

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

    = "flow { this: "FlowCollector "-> this@map.collect { value !-> this.emit(transform(value)) } } HOW DOES IT WORK? FLOW
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. API

  35. OPERATORS

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

    flattenConcat merge flattenMerge combineLatest combine skip drop onError* catch ... ...
  37. MISSING IN FLOW

  38. MISSING IN FLOW ✦ groupBy ✦ publish ✦ share ✦

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

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

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

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

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

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

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

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

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

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

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

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

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

    } .subscribeOn(ioScheduler) .observeOn(computationScheduler) .map { it * 10 } .observeOn(mainScheduler) .subscribe { println(it) } THREADING. REAKTIVE
  52. "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. 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
  55. 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
  56. 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
  57. 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
  58. 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
  59. 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
  60. 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
  61. 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
  62. 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
  63. 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
  64. SCHEDULERS

  65. 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()
  66. 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()
  67. 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
  68. SUBJECTS

  69. SUBJECTS ✦ RxJava2: Behavior, Publish, Replay

  70. SUBJECTS ✦ RxJava2: Behavior, Publish, Replay ✦ Reaktive: Behavior, Publish

  71. SUBJECTS ✦ RxJava2: Behavior, Publish, Replay ✦ Reaktive: Behavior, Publish

    ✦ Flow: Behavior (Con!flatedBroadcastChannel) DataFlow Proposal: github.com/Kotlin/kotlinx.coroutines/pull/1354
  72. 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
  73. 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
  74. USER MANUAL

  75. INTEROP & GRADUAL ADOPTION

  76. INTEROP & GRADUAL ADOPTION 1. Connect components.

  77. INTEROP & GRADUAL ADOPTION 1. Connect components. Adapter artifacts:

  78. INTEROP & GRADUAL ADOPTION 1. Connect components. Adapter artifacts: •

    org.jetbrains.kotlinx:kotlinx-coroutines-rx2
  79. INTEROP & GRADUAL ADOPTION 1. Connect components. Adapter artifacts: •

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

    org.jetbrains.kotlinx:kotlinx-coroutines-rx2 • com.badoo.reaktive:coroutines-interop • com.badoo.reaktive:rxjava2-interop
  81. INTEROP & GRADUAL ADOPTION

  82. INTEROP & GRADUAL ADOPTION 2. Convert components.

  83. INTEROP & GRADUAL ADOPTION 2. Convert components. Check this out:

    kotlinx-coroutines-core/common/src/!flow/Migration.kt
  84. 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
  85. dataObservable() ."flatMap { crashMap(it) } .subscribe( onNext = { handle(it)

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

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

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

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

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

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

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

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

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

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

    { report(it) } .collect() ERROR HANDLING BITS. FLOW Last in the chain
  96. 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!
  97. KOTLIN NATIVE

  98. KOTLIN NATIVE. FLOW

  99. KOTLIN NATIVE. FLOW ✦ Single-threaded coroutines github.com/Kotlin/kotlinx.coroutines/issues/462

  100. KOTLIN NATIVE. FLOW ✦ Single-threaded coroutines ✦ Single-threaded flows github.com/Kotlin/kotlinx.coroutines/issues/462

  101. KOTLIN NATIVE. FLOW ✦ Single-threaded coroutines ✦ Single-threaded flows github.com/Kotlin/kotlinx.coroutines/issues/462

  102. KOTLIN NATIVE. REAKTIVE

  103. KOTLIN NATIVE. REAKTIVE ✦ Workers-based threading

  104. KOTLIN NATIVE. REAKTIVE ✦ Workers-based threading ✦ Native freeze for

    switching threads, i.e. almost always
  105. KOTLIN NATIVE. REAKTIVE ✦ Workers-based threading ✦ Native freeze for

    switching threads, i.e. almost always ✦ threadLocal operator to stop freezing
  106. 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
  107. PERFORMANCE

  108. 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
  109. 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
  110. 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
  111. 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
  112. 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
  113. 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
  114. 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
  115. 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
  116. 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
  117. 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
  118. 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
  119. CONCLUSIONS

  120. CONCLUSIONS Flow ✦ Different mental model

  121. CONCLUSIONS Flow ✦ Different mental model ✦ Native? Not yet

    ready for MPP
  122. CONCLUSIONS Flow ✦ Different mental model ✦ Native? Not yet

    ready for MPP ✦ Not all operators we are used to are available
  123. 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
  124. 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
  125. 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
  126. 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
  127. 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
  128. 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
  129. 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
  130. 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
  131. 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
  132. 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 *
  133. 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
  134. 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
  135. 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
  136. RX IN MULTIPLATFORMLAND @colriot Sergey Ryabov Facebook