Slide 1

Slide 1 text

Rx by example
 The Multicast edition Vol. 3

Slide 2

Slide 2 text

Multicasting What is Why How

Slide 3

Slide 3 text

Multicasting 2 ways to do ConnectableObservables Subjects

Slide 4

Slide 4 text

Multicasting with ConnectableObservables

Slide 5

Slide 5 text

val source: Observable = Observable.interval(1, TimeUnit.SECONDS) source.subscribe { Log.v(TAG, "subscriber 1 : received event $it") } subscriber 1 : received event 0 subscriber 2 : received event 0 subscriber 1 : received event 1 subscriber 2 : received event 1 subscriber 1 : received event 2 subscriber 2 : received event 2 subscriber 1 : received event 3 subscriber 2 : received event 3 subscriber 1 : received event 4 subscriber 2 : received event 4 source.subscribe { Log.v(TAG, "subscriber 2 : received event $it") }

Slide 6

Slide 6 text

val source: Observable = Observable.interval(1, TimeUnit.SECONDS) Thread.sleep(1000) source.subscribe { Log.v(TAG, "subscriber 1 : received event $it") } source.subscribe { Log.v(TAG, "subscriber 2 : received event $it") } subscriber 1 : received event 0 Skipped 59 frames! app may be doing too much work on its main thread. subscriber 1 : received event 1 subscriber 2 : received event 0 subscriber 1 : received event 2 subscriber 2 : received event 1 subscriber 1 : received event 3 subscriber 2 : received event 2 subscriber 1 : received event 4 subscriber 2 : received event 3

Slide 7

Slide 7 text

val source: Observable = Observable.interval(1, TimeUnit.SECONDS) Thread.sleep(1000) source.subscribe { Log.v(TAG, "subscriber 1 : received event $it") } source.subscribe { Log.v(TAG, "subscriber 2 : received event $it") } ConnectableObservable = .publish() source.connect() Skipped 59 frames! app may be doing too much work on its main thread. subscriber 1 : received event 0 subscriber 2 : received event 0 subscriber 1 : received event 1 subscriber 2 : received event 1 subscriber 1 : received event 2 subscriber 2 : received event 2 subscriber 1 : received event 3 subscriber 2 : received event 3

Slide 8

Slide 8 text

.publish().refcou .replay().autoCo .share().cache( .replayingShar blish().refcoun eplay().autoConnect(n share().cache() .replayingShare() h().refcount() y().autoConnect(n) ).cache() are() .publish().refcount .replay().autoConn re().cache() ngShare( Multicasting operator permutations

Slide 9

Slide 9 text

val sourceObservable = Observable.interval(1, TimeUnit.SECONDS)
 .doOnSubscribe{ log("observable (subscribed)") }
 .doOnDispose{ log("observable (disposed)") }
 disposable1?.let {
 it.dispose()
 log("subscriber 1 disposed")
 disposable1 = null
 return
 } @OnClick(R.id.btn_1)
 fun onBtn1Click() { disposable1 = sharedObservable.subscribe({ long -> _log(“subscriber 1: onNext $long") }) } https://github.com/kaushikgopal/RxJava-Android-Samples/tree/kg/feat/connect_playground

Slide 10

Slide 10 text

val sourceObservable = Observable.interval(1, TimeUnit.SECONDS) sourceObservable.connect() .doOnSubscribe{ log("observable (subscribed)") }
 .doOnDispose{ log("observable (disposed)") } .publish() https://github.com/kaushikgopal/RxJava-Android-Samples/tree/kg/feat/connect_playground

Slide 11

Slide 11 text

val sourceObservable = Observable.interval(1, TimeUnit.SECONDS) .doOnSubscribe{ log("observable (subscribed)") }
 .doOnDispose{ log("observable (disposed)") } sourceObservable .refCount() .publish() https://github.com/kaushikgopal/RxJava-Android-Samples/tree/kg/feat/connect_playground

Slide 12

Slide 12 text

sourceObservable .publish().refCount()

Slide 13

Slide 13 text

sourceObservable .publish().refCount() subscribe 1 subscriber so connect automatically 0 0 onNext onNext 1 1 onNext onNext subscribe 2 subscribers 2 onNext onNext 2 onNext 2 3 onNext onNext 3 onNex 3 refcount()

Slide 14

Slide 14 text

ribers 2 onNext onNext 2 onNext 2 3 onNext onNext 3 onNext 3 refcount() unsubscribe X unsubscribe 1 subscriber left 4 4 onNext onNext unsubscribe X X 0 subscribers left

Slide 15

Slide 15 text

ext refcount() unsubscribe X unsubscribe 1 subscriber left 4 4 onNext onNext X X 0 subscribers left subscribe 0 0 onNext onNext YO LISTEN UP !! THIS IS IMPORTANT

Slide 16

Slide 16 text

sourceObservable .publish().autoConnect(2)

Slide 17

Slide 17 text

sourceObservable .publish().autoConnect(2) .autoConnect(2) subscribe 1 subscriber (waiting for 2) subscribe 2 subscribers 0 onNext onNext 0 onNext 0 1 onNext onNext 1 onNext 1 unsubscribe X unsubscribe 1 subscriber left

Slide 18

Slide 18 text

sourceObservable .publish().autoConnect(2) ext Next Next 1 onNext onNext 1 onNext 1 X unsubscribe 1 subscriber left .autoConnect(2) 2 2 onNext onNext unsubscribe X 0 subscribers left 3 4 YO Me AGAIN !! THIS IS IMPORTANT

Slide 19

Slide 19 text

sourceObservable .publish().autoConnect(2) be ber left 2 2 onNext onNext unsubscribe X 0 subscribers left 3 4 .autoConnect(2) subscribe 5 5 onNext onNext first event seen is 5

Slide 20

Slide 20 text

Recap .publish() .autoConnect(n) .refcount() Creates ConnectableObservable Requires .connect() or something else to kickstart Source subscription started right away Source is “restarted” when all subscribers leave and 
 new subscribers come in Source subscription started
 only after n subscribers start Source never stops once started (even if all subscribers leave)

Slide 21

Slide 21 text

.publish() Creates ConnectableObservable .replay(n) Creates ConnectableObservable Another way to create ConnectableObservables Start with last “n” events “n” defaults to all events

Slide 22

Slide 22 text

Pop Quiz ! .publish().refCount() .publish().autoConnect(1) Vs a.k.a .share()

Slide 23

Slide 23 text

Pop Quiz ! .replay(1).refCount() .replay(1).autoConnect(1) Vs ~ a.k.a .cache() *

Slide 24

Slide 24 text

sourceObservable .replay(1).refCount() .replay(1) .refCount() subscribe 1 subscriber so connect automatically 0 0 onNext onNext subscribe 2 subscribers onNext 0 unsubscribe X unsubscribe X X subscribe No replay!

Slide 25

Slide 25 text

sourceObservable .replay(1).refCount() .replay(1) .autoConnect() subscribe 1 subscriber so connect automatically 0 0 onNext onNext subscribe 2 subscribers onNext 0 unsubscribe X unsubscribe X subscribe onNext 0

Slide 26

Slide 26 text

Pop Quiz ! .replayingShare() = .replay(1).refcount() But remembers the last item even when restarted .replay(1).refCount()

Slide 27

Slide 27 text

Use cases Let’s get real

Slide 28

Slide 28 text

Location provider Use case 1 return RxLocation(context) .updates(locationRequest) // multicast? .publish() .autoConnect(n) .replay(1) .refcount() .replayingShare() .replay(1) .refcount()

Slide 29

Slide 29 text

Use case 2 Persist a network call beyond activity rotation ViewModels baby ...

Slide 30

Slide 30 text

Persist a network call beyond activity rotation class RotationPersistActivity : MyBaseActivity() { lateinit var sharedViewModel: SharedViewModel override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 sharedViewModel = ViewModelProviders.of(activity) .get(SharedViewModel::class.java) 
 }
 
 @OnClick(R.id.btn_rotate_persist)
 fun displayEventsOnBtnClick() {
 // … compositeDisposables += sharedViewModel
 .networkRequest()
 .subscribe({ l ->
 Use case 2

Slide 31

Slide 31 text

class RotationPersistActivity : MyBaseActivity() { lateinit var sharedViewModel: SharedViewModel override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 sharedViewModel = ViewModelProviders.of(activity) .get(SharedViewModel::class.java) 
 }
 
 @OnClick(R.id.btn_rotate_persist)
 fun displayEventsOnBtnClick() {
 // … compositeDisposables += sharedViewModel
 .networkRequest()
 .subscribe({ l ->
 log("Received element $l")
 }) } } Persist a network call beyond activity rotation Use case 2

Slide 32

Slide 32 text

Persist a network call beyond activity rotation override fun onCleared() {
 super.onCleared()
 // cleanup } Use case 2 
 fun networkRequest(): Observable {
 
 // We’ll fill this later } 
 class SharedViewModel : ViewModel() {


Slide 33

Slide 33 text

Use case 2 Persist a network call beyond activity rotation 
 fun networkRequest(): Observable {
 
 // We’ll fill this later } override fun onCleared() {
 super.onCleared()
 // cleanup } var sharedStream: Observable = someApi.makeRequest() //.map(…) .replay(1) .autoConnect() 
 class SharedViewModel : ViewModel() {
 return sharedStream

Slide 34

Slide 34 text

Persist a network call beyond activity rotation Use case 2 
 fun networkRequest(): Observable {
 
 } override fun onCleared() {
 super.onCleared()
 // cleanup } var sharedStream: Observable = someApi.makeRequest() //.map(…) .replay(1) .autoConnect() 
 class SharedViewModel : ViewModel() {
 return sharedStream

Slide 35

Slide 35 text

Persist a network call beyond activity rotation .replay(1) .autoConnect(1, disposable -> { myDisposable = disposable }) var myDisposable: Disposable? = null myDisposable?.dispose 
 class SharedViewModel : ViewModel() {
 var sharedStream: Observable = someApi.makeRequest() //.map(…) override fun onCleared() {
 super.onCleared()
 // cleanup } fun networkRequest(): Observable {
 return sharedStream } Use case 2

Slide 36

Slide 36 text

_paginator .map(items ->{ //do something }) .onBackPressureDrop() .doOnNext(
 i -> {
 _progressBar.setVisibility(View.VISIBLE);
 }) .concatMap(this::itemsFromNetworkCall) .observeOn(AndroidSchedulers.mainThread()) .map(items -> {
 _adapter.addItems(items);
 _adapter.notifyDataSetChanged();
 return items;
 })
 .subscribe(strings -> {
 _progressBar.setVisibility(View.INVISIBLE);
 });
 Use case 3 Reduce side effects

Slide 37

Slide 37 text

.concatMap(this::itemsFromNetworkCall) .observeOn(AndroidSchedulers.mainThread()) .map(items -> {
 _adapter.addItems(items);
 _adapter.notifyDataSetChanged();
 return items;
 })
 .subscribe(strings -> {
 _progressBar.setVisibility(View.INVISIBLE);
 });
 Use case 3 Reduce side effects .doOnNext(
 i -> {
 _progressBar.setVisibility(View.VISIBLE);
 }) _paginator .map(items ->{ //do something }) .onBackPressureDrop()

Slide 38

Slide 38 text

.doOnNext(
 i -> {
 _progressBar.setVisibility(View.VISIB }) Use case 3 Reduce side effects _paginator .map(items ->{ //do something }) .onBackPressureDrop() .publish() ConnectableObservable shared = shared shared .subscribe(
 i -> {
 _progressBar.setVisibility(View.VISIB }) .concatMap(this::itemsFromNetworkCall) .observeOn(AndroidSchedulers.mainThread()) .map(items -> {
 _adapter.addItems(items);
 _adapter.notifyDataSetChanged();
 return items;
 })
 .subscribe(strings -> {
 _progressBar.setVisibility(View.INVISIBLE);
 });


Slide 39

Slide 39 text

.concatMap(this::itemsFromNetworkCall) .observeOn(AndroidSchedulers.mainThread()) .map(items -> {
 _adapter.addItems(items);
 _adapter.notifyDataSetChanged();
 return items;
 })
 .subscribe(strings -> {
 _progressBar.setVisibility(View.INVISIBLE);
 });
 Use case 3 Reduce side effects _paginator .map(items ->{ //do something }) .onBackPressureDrop() .publish() ConnectableObservable shared = shared.connect(); .subscribe(
 i -> {
 _progressBar.setVisibility(View.VISIBLE);
 }) shared shared

Slide 40

Slide 40 text

✌ fragmentedpodcast.com @kaushikgopal kaush.co We're hiring Android devs! tech.instacart.com

Slide 41

Slide 41 text

Guided Q&A

Slide 42

Slide 42 text

I'm worried about Multicasting leaking things around? Should I be? If multi-casted stream "errors"/"terminates" What happens to subscribers? How do I dispose of ConnectableObs. when using autoConnect? Where Multicasting begins (at which point of the Observable) before/after publish ? When should I use ConnectableObsv. vs Subjects ? 1 2 3 4 5