Slide 1

Slide 1 text

Learning Rx
 by example

Slide 2

Slide 2 text

2 mt intro to Rx 3 (intermediate) examples 1
 2

Slide 3

Slide 3 text

2 mt Intro to Rx

Slide 4

Slide 4 text

API for Asynchronous programming

Slide 5

Slide 5 text

Observer pattern done right Best ideas from: Observer pattern Iterator pattern Functional programming

Slide 6

Slide 6 text

Observer pattern done right Observable.just(1) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(getSubscriber()); .useSomeFunkyOperators()

Slide 7

Slide 7 text

Example 1 Loading from Disk Cache + Network Call

Slide 8

Slide 8 text

Cached data
 Fast Example 1 Database Fresh data
 Slow Server Requirement:

Slide 9

Slide 9 text

.concatEager( .merge( Example 1 Code: Observable .subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThrd())
 .subscribe(); .concatEager getDiskResults(), getNetworkResults()) .merge

Slide 10

Slide 10 text

.concatEager( .merge( Example 1 Code: Observable .subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThrd())
 .subscribe(); .concatEager getDiskResults(), getNetworkResults()) .merge Both of these methods return Observable

Slide 11

Slide 11 text

Example 1 Database Network request Problem: .merge

Slide 12

Slide 12 text

Example 1 Database Network request Problem: .merge

Slide 13

Slide 13 text

Example 1 Code: .merge Observable .subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThrd())
 .subscribe(); .merge( getDiskResults(), getNetworkResults()) .subscribe(new Subscriber() {
 @Override
 public void onCompleted() { //... }
 
 @Override
 public void onError(Throwable e) { //... }
 
 @Override
 public void onNext(Result result) {
 if ( list.contains(result) && isExistingResultFromNetwork(result))
 return;
 // usual "add result to list" logic
 list.add(result); list.refresh();
 }
 });

Slide 14

Slide 14 text

Example 1 Code: .merge Observable .subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThrd())
 .subscribe(); .merge( getDiskResults(), getNetworkResults()) .subscribe(new Subscriber() {
 @Override
 public void onCompleted() { //... }
 
 @Override
 public void onError(Throwable e) { //... }
 
 @Override
 public void onNext(Result result) {
 if ( list.contains(result) && isResultFromNetwork(result))
 return;
 // usual "add result to list" logic
 list.add(result); list.refresh();
 }
 }); NO!
 The Rx is not strong with this code https://twitter.com/JakeWharton/status/786363146990649345

Slide 15

Slide 15 text

Example 1 Code: getNetworkData() .publish( network -> ) getDiskData() .takeUntil(network) Observable .merge( network, )

Slide 16

Slide 16 text

Example 1 Code: Get database results but stop after network results getDiskResults() .takeUntil(getNetworkResults()) getDiskButStopAfterNetwork() =

Slide 17

Slide 17 text

Example 1 Code: Get disk results that occur before Network starts + Network results getDiskResults() .takeUntil(getNetworkResults()) getDiskButStopAfterNetwork() = getDiskButStopAfterNetwork() ) getNetworkResults() Observable .merge(

Slide 18

Slide 18 text

Example 1 Code: Get disk results that occur before Network starts + Network results ) .publish( getNetworkResults() getDiskResults() .takeUntil(getNetworkResults()) Observable .merge(

Slide 19

Slide 19 text

Example 1 Code: Get disk results that occur before Network starts + Network results Observable .merge( , ) .publish( getNetworkResults() network -> getNetworkResults() getDiskResults() .takeUntil(getNetworkResults())

Slide 20

Slide 20 text

Example 1 Code: Get disk results that occur before Network starts + Network results Observable .merge( , .publish( getNetworkResults() network -> network, ) getDiskResults() .takeUntil(getNetworkResults())

Slide 21

Slide 21 text

Example 1 Code: Get disk results that occur before Network starts + Network results Observable .merge( , .publish( getNetworkResults() network -> ) getDiskResults() .takeUntil(getNetworkResults()) network network,

Slide 22

Slide 22 text

Example 1 Code: Observable .subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThrd())
 .subscribe(); .concat( getSlowCachedDiskData(), getFreshNetworkData()) .publish getNetworkData()
 .publish( network ->
 Observable .merge( network, getDiskData() .takeUntil(network) ) )

Slide 23

Slide 23 text

Example 2 Pagination
 (using Subjects)

Slide 24

Slide 24 text

Example 2b Setup starting seq. once
 (network call for results) Observable Observable
 .just(1)
 .flatMap(pageNo -> getNetworkResults(pageNo))

Slide 25

Slide 25 text

compose Observable Observable map R T flatmap R Observable Holy trinity of RxConversion

Slide 26

Slide 26 text

Example 2b Setup starting seq. once
 (network call for results) Subscribe here Observable Observable
 .just(1)
 .flatMap(pageNo -> getNetworkResults(pageNo)) .subscribe(new Subscriber>() {
 @Override
 public void onCompleted() {
 // all items downloaded
 }
 
 @Override
 public void onError(Throwable e) {
 // handle error
 }
 
 @Override
 public void onNext(List items) {
 
 }
 }); private Observable> getNetworkResults(int pageNo) {
 // make network request // get List of Items for page number
 } addToList(items);

Slide 27

Slide 27 text

Example 2b Setup starting seq. once
 (network call for results) Subscribe here Observable Observable
 .just(1)
 .flatMap(pageNo -> getNetworkResults(pageNo)) .subscribe(new Subscriber>() {
 @Override
 public void onCompleted() {
 // all items downloaded
 }
 
 @Override
 public void onError(Throwable e) {
 // handle error
 }
 
 @Override
 public void onNext(List items) {
 
 }
 }); .concatMap(pageNo -> getNetworkResults(pageNo)) addToList(items);

Slide 28

Slide 28 text

Example 2b Setup starting seq. once
 (network call for results) Subscribe here like page numbers! Observable What if I could keep feeding inputs to get the next set of results?

Slide 29

Slide 29 text

Network call 
 to get results for page n
 Subject Code: PublishSubject paginator = PublishSubject.create(); Example 2b Observable
 .just(1)
 .concatMap(pgNo -> getNetworkResults(pgNo)) paginator .observeOn(AndroidSchedulers.mainThread())
 .subscribe(new Subscriber>() {
 @Override
 public void onCompleted() {
 // all items downloaded
 }
 
 @Override
 public void onError(Throwable e) {
 // handle error
 }
 
 @Override
 public void onNext(List items) {
 
 }
 }); addToList(items);

Slide 30

Slide 30 text

Network call 
 to get results for page n
 Subscribe here Example 2a Code: Subject void onReachedEndOfList() {
 //...
 
 } paginator.onNext(nextPage); .concatMap(pgNo -> getNetworkResults(pgNo)) paginator .observeOn(AndroidSchedulers.mainThread())
 .subscribe(new Subscriber>() {
 @Override
 public void onCompleted() {
 // all items downloaded
 }
 
 @Override
 public void onError(Throwable e) {
 // handle error
 }
 
 @Override
 public void onNext(List items) {
 
 }
 }); addToList(items);

Slide 31

Slide 31 text

Example 3 TrueTime: Implementing NTP with Rx github.com/instacart/truetime-android

Slide 32

Slide 32 text

Example 3 TrueTime NTP
 
 Pool DNS
 Resolve SNTP Rq 2 SNTP Request 1 SNTP Rq 3 SNTP Rq 4 SNTP Rq 5 Least
 roundtrip delay 5 IP1 IP2 IP3 IP4 Sort by clock offset + Pick Median

Slide 33

Slide 33 text

Example 3 TrueTime NTP
 
 Pool DNS
 Resolve SNTP Rq 2 SNTP Request 1 SNTP Rq 3 SNTP Rq 4 SNTP Rq 5 Least
 roundtrip delay 5 IP1 IP2 IP3 IP4 TrueTime Sort by clock offset + Pick Median

Slide 34

Slide 34 text

Observable
 .just(ntpPool)
 .compose(resolveNtpPool())
 NTP DNS
 Resolve time.apple.com Code: compose Observable Observable IP1 IP2 IP3 IP4

Slide 35

Slide 35 text

Observable
 .just(ntpPool)
 .compose(resolveNtpPool())
 NTP DNS
 Resolve time.apple.com Code: private Transformer resolveNtpPool(){
 return ntpPool -> {
 try {
 return Observable.from( InetAddress.getAllByName(ntpPool)); } catch (UnknownHostException e) {
 return Observable.error(e);
 }
 })
 }


Slide 36

Slide 36 text

Observable
 .just(ntpPool)
 .compose(resolveNtpPool())
 NTP DNS
 Resolve time.apple.com Code: IP1

Slide 37

Slide 37 text

Code: Observable
 .just(ntpPool)
 .compose(resolveNtpPool())
 .flatMap(bestResponseAgainstSingleIp())
 IP1 T (IP1) Observable (SNTP response) Request 1 SNTP Request 2 SNTP Request 3 SNTP Request 4 SNTP Request 5 SNTP

Slide 38

Slide 38 text

compose Observable Observable map R T flatmap T Observable Holy trinity of RxConversion

Slide 39

Slide 39 text

Code: IP1 Observable
 .just(ntpPool)
 .compose(resolveNtpPool())
 .flatMap(bestResponseAgainstSingleIp())
 bestResponseAgainstSingleIp() Func1 > return Observable .just(singleIp)
 .repeat(5) { Request 1 SNTP Request 2 SNTP Request 3 SNTP Request 4 SNTP Request 5 SNTP

Slide 40

Slide 40 text

Code: .flatMap(ipAddress -> sntpNtwrkReq(ipAddress)) Observable
 .just(ntpPool)
 .compose(resolveNtpPool())
 .flatMap(bestResponseAgainstSingleIp())
 bestResponseAgainstSingleIp() return Observable .just(singleIp)
 .repeat(5) { IP1 Request 1 SNTP Request 2 SNTP Request 3 SNTP Request 4 SNTP Request 5 SNTP Func1 >

Slide 41

Slide 41 text

Code: IP1 Observable
 .just(ntpPool)
 .compose(resolveNtpPool())
 .flatMap(bestResponseAgainstSingleIp())
 bestResponseAgainstSingleIp() return Observable .just(singleIp)
 .repeat(5) { 5 .flatMap(ipAddress -> sntpNtwrkReq(ipAddress)) Request 1 SNTP Request 2 SNTP Request 3 SNTP Request 4 SNTP Request 5 SNTP 
 .doOnError(throwable -> {
 // this request alone failed // retry this req alone
 })
 .retry(5)) } Func1 >

Slide 42

Slide 42 text

Code: IP1 Observable
 .just(ntpPool)
 .compose(resolveNtpPool())
 .flatMap(bestResponseAgainstSingleIp())
 bestResponseAgainstSingleIp() return Observable .just(singleIp)
 .repeat(5) { 5 .flatMap(ipAddress -> sntpNtwrkReq(ipAddress)) Request 1 SNTP Request 2 SNTP Request 3 SNTP Request 4 SNTP Request 5 SNTP } Func1 > 
 .doOnError(throwable -> {
 // this request alone failed // retry this req alone
 })
 .retry(5)) ) 
 .toList()

Slide 43

Slide 43 text


 .map(responseList -> {
 Collections.sort(responseList, comparator); 
 return responseList.get(0); }) Least
 roundtrip delay Code: 
 .toList() Observable
 .just(ntpPool)
 .compose(resolveNtpPool())
 .flatMap(bestResponseAgainstSingleIp())
 bestResponseAgainstSingleIp() Func1 > { } 
 .doOnError(throwable -> {
 // this request alone failed // retry this req alone
 })
 .retry(5)) )

Slide 44

Slide 44 text

Example 3 TrueTime NTP
 
 Pool DNS
 Resolve SNTP Rq 2 SNTP Request 1 SNTP Rq 3 SNTP Rq 4 SNTP Rq 5 Least
 roundtrip delay 5 IP1 IP2 IP3 IP4 Sort by clock offset + Pick Median

Slide 45

Slide 45 text

Example 3 TrueTime NTP
 
 Pool DNS
 Resolve SNTP Rq 2 SNTP Request 1 SNTP Rq 3 SNTP Rq 4 SNTP Rq 5 5 IP1 IP2 IP3 IP4 Sort by clock offset + Pick Median

Slide 46

Slide 46 text

.map(filterMedianResponse()) Code: Observable
 .just(ntpPool)
 .compose(resolveNtpPool())
 .flatMap(bestResponseAgainstSingleIp())
 .toList() filterMedianResponse() Func1 , Response> return responseList -> { } Collections.sort(responses, comparator);
 Sort by clock offset return bestResponses .get(bestResponses.size() / 2); Pick Median Sort by clock offset + Pick Median

Slide 47

Slide 47 text

Observable
 .just(ntpPool)
 .compose(resolveNtpPool())
 .flatMap(bestResponseAgainstSingleIp())
 .doOnNext(response -> convertToTime(response)); .map(filterMedianResponse()) .toList() Code: TrueTime Sort by clock offset + Pick Median

Slide 48

Slide 48 text

Example 3 TrueTime NTP
 
 Pool DNS
 Resolve SNTP Rq 2 SNTP Request 1 SNTP Rq 3 SNTP Rq 4 SNTP Rq 5 Least
 roundtrip delay 5 IP1 IP2 IP3 IP4 TrueTime Sort by clock offset + Pick Median

Slide 49

Slide 49 text

✌ fragmentedpodcast.com tech.instacart.com @kaushikgopal kaush.co My thanks to @cyrilmotier who graciously allowed me to rip-off his slide deck theme