Rxify - a simple spell for complex RxJava operators

A3958eeb9a7f402b134c0c017d6614ee?s=47 ragdroid
November 10, 2016

Rxify - a simple spell for complex RxJava operators

Droidcon India, Bangalore 2016
This talk is about the operators which upon reading the document look daunting at first but if you get a grasp of them, they can be quite useful in almost all the apps you code. RxJava is your friend, it will make your life easier. With RxJava many complex tasks can be accomplished easily and without errors. As the title says, `Rxify` - you just need to cast the spells and all your work will be done. From hitting the cache-first to performing retries, just cast the spells and you are good to go. https://medium.com/@ragdroid/rxify-a-simple-spell-for-complex-rxjava-operators-part-1-4c31921583c4#.6hxqs39e6

A3958eeb9a7f402b134c0c017d6614ee?s=128

ragdroid

November 10, 2016
Tweet

Transcript

  1. `Rxify` Presented By : Garima Jain @ragdroid A simple spell

    for complex RxJava operators
  2. Who?

  3. `Rxify` is for? • Already working on RxJava - More

    to learn.
  4. `Rxify` is for? • Already working on RxJava - More

    to learn. • Beginner - Time to start Rxifying your apps
  5. `Rxify` is for? • Already working on RxJava - More

    to learn. • Beginner - Time to start Rxifying your apps • Pros - Talk will be fun!
  6. What?

  7. • `Rxify` - term signifies use of RxJava • RxJava

    makes our lives simpler. • Cast a spell (use an operator) and we are good to go. `Rxify`
  8. • Reactive Extensions for JVM (Java Virtual Machine). • Observer

    pattern for sequences of data / events. RxJava
  9. RxJava makes lives simpler • Never having to worry about

    : • Low-level Threading • Synchronisation • Background Processing
  10. Why?

  11. RxJava operators at first glance

  12. RxJava operators at first glance

  13. RxJava operators at first glance • Complex • Hard to

    understand • Marble Diagrams? What!!
  14. `Rxify` your apps!

  15. Basics

  16. • Observable produces items, • Subscriber consumes those items. •

    Observable calls : (Actions) • onNext() multiple times • followed by onComplete() • OR onError() Basics
  17. • Operators : • manipulate items between producer and consumer.

    • Schedulers • Main Thread • Computation (Background Thread) • Test Scheduler Basics
  18. Observable Pattern Producer Consumer A B C items Observable emits

    Subscriber consumes
  19. Just-ify Simple Observable String Hello World! Observable.just("Hello World!”); Observable <String>

    time Hello World
  20. A B C A B C Rx-ify String Array Simple

    Observable Observable.from(new String[] { "A", "B", “C"}); Observable <String>
  21. “Spells” operators

  22. “Map-io” map() operator

  23. Map Transforms every element of a collection of items.

  24. Map “Transform the items emitted by an Observable by applying

    a function to each item” - source Transforms every element of a collection of items.
  25. Map-io Observable <Integer> Map Observable <Integer> 1 2 3 10

    20 30
  26. Observable <Integer> Map Observable.from(new Integer[] { 1, 2, 3})
 .map(new

    Func1<Integer, Integer>() {
 @Override
 public Integer call(Integer integer) {
 return integer * 10;
 }
 }); Observable <Integer> 1 2 3 10 20 30
  27. Observable <Integer> Map Observable <Integer> 1 2 3 10 20

    30 via-Hedwig In RxJava 2 Func1 has become Function Func2 has become BiFunction
  28. Observable <Integer> Map Observable.from(new Integer[] { 1, 2, 3})
 .map(new

    Function<Integer, Integer>() {
 @Override
 public Integer apply(Integer integer) {
 return integer * 10;
 }
 }); Observable <Integer> 1 2 3 10 20 30
  29. “FlatMap-ium” flatMap() operator

  30. FlatMap Transforms every element of a collection into another collection

    of items and combines them into a single collection.
  31. FlatMap “Transform the items emitted by an Observable into Observables,

    then flatten the emissions from those into a single Observable” - source Transforms every element of a collection into another collection of items and combines them into a single collection.
  32. FlatMap-ium Observable <String> FlatMap Observable <String> Hello World How Are

    You? Hello World How Are You?
  33. Observable <String> FlatMap Observable.from(new String[] {"Hello World", "How Are You"})


    .flatMap(new Function<String, Observable<String>>() {
 @Override
 public Observable<String> apply(String s) {
 return Observable.from(s.split(" "));
 }
 }); Observable <String> Hello World How Are You? Hello World How Are You?
  34. “Filter-rum” filter() operator

  35. Filter Filter the original collection to emit items that satisfy

    a condition.
  36. Filter “Emit only those items from an Observable that pass

    a predicate test" - source Filter the original collection to emit items that satisfy a condition.
  37. Filter-rum Observable <Integer> Filter Observable <Integer> 1 2 3 1

    4 5 6 3 5 7 7
  38. Observable <Integer> Filter Observable <Integer> 1 2 3 1 4

    5 6 3 5 getSourceObservable().filter(new Function<Integer, Boolean>() {
 @Override
 public Boolean apply(Integer integer) {
 return integer % 2 == 1;
 }
 }); 7 7
  39. “Comples spells” complex operators

  40. “Zip-pyosa” zip() operator

  41. Zip Combine multiple collections together according to some function and

    emit the result of each combination.
  42. Zip Combine multiple collections together according to some function and

    emit the result of each combination. “Combine the emissions of multiple Observables together via a specified function and emit single items for each combination based on the results of this function" - source
  43. Zip Marble Diagram

  44. Zip Marble Diagram Looks a little Overwhelming

  45. Zip Marble Diagram Looks a little Overwhelming

  46. Fictional Problem (Polyjuice Potion) • Hermione wants to prepare Polyjuice

    potion (P = R + H). * • She is waiting for task Ron (R) to bring FluxWeed. • She is waiting for task Harry (H) to bring hair. • Both tasks R and H are executing asynchronously. • Only after both R and H are completed then P can start. * ‘+’ is some complex function (brewing the Polyjuice in this case)
  47. Zip-Yosa Observable <FluxWeed> Zip Observable <Polyjuice> FluxWeed CrabHair Polyjuice Potion

    Observable <CrabHair>
  48. Observable <FluxWeed> Zip Observable <Polyjuice> FluxWeed CrabHair Polyjuice Potion Observable

    <CrabHair> 
 return Observable.zip(fluxWeedObservable, hairObservable,
 new BiFunction<FluxWeed, CrabHair, PolyJuice>() {
 @Override
 public PolyJuice apply(final FluxWeed fluxWeed,
 final CrabHair crabHair) {
 return new PolyJuice(fluxWeed, crabHair)
 .prepare();
 }
 });
  49. Can you relate the Polyjuice Potion Problem to any coding

    problem?
  50. Can you relate the Polyjuice Portion Problem to any coding

    problem? YES! When multiple API calls are executing simultaneously.
  51. “Concat-ify” concat() operator “Merge-os” merge() operator VS

  52. Concat “Emit the emissions from two or more Observables without

    interleaving them” source “Combine multiple Observables into one by merging their emissions” source Merge
  53. Concat Merge 1 2 3 4 8 9 1 2

    3 4 8 9 1 2 3 4 8 9 1 2 3 4 8 9 Concat-ify Merge-os
  54. Fictional Problem (Snape’s Assignment) • Professor Snape has requested all

    students to write an essay on werewolves. • The students who will turn in the essay first will get more points than the ones submitting later. • Students are divided into four house observables : • GryffindorObservable (G), • SlytherinObservable (S), • HufflepuffObservable (H) and • RavenclawObservable(R)).
  55. Observable <Gryffindor> Snape’s Assignment - Submission R1 R2 H1 H2

    Observable <Ravenclaw> Observable <Hufflepuff> G1 G2 S1 S2 Observable <Slytherin>
  56. Observable<Student> concatify() {
 Observable<Student> slytherinObservable = getObservable(House.SLYTHERIN);
 Observable<Student> huffleObervable =

    getObservable(House.HUFFLEPUFF);
 Observable<Student> ravenObservable = getObservable(House.RAVENCLAW);
 Observable<Student> gryffindorObservable = getObservable(House.GRYFFINDOR);
 return Observable.concat(
 slytherinObservable,
 huffleObervable,
 ravenObservable,
 gryffindorObservable);
 } Snape’s Assignment - Draco’s trick
  57. Snape’s Assignment - Draco’s trick S1 S2 H1 H2 R1

    R2 G1 G2 Concat-ify Observable <Gryffindor> R1 R2 H1 H2 Observable <Ravenclaw> Observable <Hufflepuff> G1 G2 S1 S2 Observable <Slytherin>
  58. Guess who is not so pleased?

  59. Guess who is not so pleased? YES!

  60. Guess who is not so pleased? YES! Poor Hermione

  61. Observable<Student> mergeos() {
 Observable<Student> slytherinObservable = getObservable(House.SLYTHERIN);
 Observable<Student> huffleObervable =

    getObservable(House.HUFFLEPUFF);
 Observable<Student> ravenObservable = getObservable(House.RAVENCLAW);
 Observable<Student> gryffindorObservable = getObservable(House.GRYFFINDOR);
 return Observable.merge(
 slytherinObservable,
 huffleObervable,
 ravenObservable,
 gryffindorObservable);
 } Snape’s Assignment - Hermione’s fix
  62. Snape’s Assignment - Hermione’s fix G1 S1 G2 R1 H1

    S2 R2 H2 Merge-os Observable <Gryffindor> R1 R2 H1 H2 Observable <Ravenclaw> Observable <Hufflepuff> G1 G2 S1 S2 Observable <Slytherin>
  63. • Hit the cache first • If data found return

    data • Else Network call Problem : Cache First Then Network
  64. • Hit the cache first • If data found return

    data • Else Network call Problem : Cache First Then Network Concat-ify
  65. • Hit the cache first • If data found return

    data • Else Network call Observable<List<Lecture>> cacheObservable = getLecturesFromCache();
 Observable<List<Lecture>> lecturesFromServer = getLecturesFromServer();
 return cacheObservable .concatWith(lecturesFromServer)
 .take(1); Problem : Cache First Then Network
  66. Problem : Cache First Then Network • Hit the cache

    first • If data found return data • Else Network call Observable<List<Lecture>> cacheObservable = getLecturesFromCache();
 Observable<List<Lecture>> lecturesFromServer = getLecturesFromCache();
 return cacheObservable .concatWith(lecturesFromServer)
 .take(1); via-Hedwig when no data, first() : NoSuchElementException : no data take(1) will complete without exception
  67. Backpressure “Defence Against the Dark Arts”

  68. Backpressure Frequency of Producer producing an item is more than

    the ability of Consumer to consume.
  69. Backpressure Frequency of Producer producing an item is more than

    the ability of Consumer to consume. “Strategies for coping with Observables that produce items more rapidly than their observers consume them” - source
  70. Backpressure via-Hedwig GOOD NEWS!

  71. Backpressure via-Hedwig Usually you do not have to worry about

    back pressure in your applications.
  72. Backpressure via-Hedwig RxJava 2 introduces Flowable which is an Observable

    with backpressure support.
  73. Backpressure via-Hedwig With RxJava2, Observables do not support backpressure while

    Flowables do.
  74. Fictional Problem (The Battle) • Dumbledore’s army (Harry, Hermione, Ron

    and others) • Death Eaters (Lucius Malfoy, Bellatrix Lestrange and others) • Each team is casting lethal spells against the other team. • Dumbledore’s army is not as experienced as the Death Eaters. • Spells produced by Death Eaters is overwhelming the Dumbledore’s army (consumer).
  75. “Buffer-undum” buffer() operator

  76. Buffer Buffer small chunks of items and emit them instead

    of emitting one item at a time.
  77. Buffer Buffer small chunks of items and emit them instead

    of emitting one item at a time. “Periodically gather items emitted by an Observable into bundles and emit these bundles rather than emitting the items one at a time" - source
  78. Buffer-undum Buffer Observable <Spell> 1 2 3 4 Observable <Spell>

    1 2 3 4
  79. Observable <Spell> Buffer Observable <Spell> 1 2 3 4 1

    2 getSpellsObservable()
 .buffer(2); //count 3 4
  80. Buffer-undum Buffer LOSS-LESS Observable <Spell> 1 2 3 4 Observable

    <Spell> 1 2 3 4
  81. Buffer Observable <Spell> 1 2 3 4 Observable <Spell> 1

    2 3 4
  82. “Debounce-y” debounce() operator

  83. Debounce From a list of items emit an item only

    when, some time has passed since it last emitted anything.
  84. Debounce From a list of items emit an item only

    when, some time has passed since it last emitted anything. “Only emit an item from an Observable if a particular timespan has passed without it emitting another item" - source
  85. Debounce-y Debounce Observable <Spell> 1 2 3 4 Observable <Spell>

    1 2 4
  86. Observable <Spell> 1 2 3 4 Observable <Spell> 1 2

    4 getSpellsObservable()
 .debounce(300, TimeUnit.MILLISECONDS); Debounce
  87. Observable <Spell> 1 2 3 4 Observable <Spell> 1 2

    4 Debounce
  88. Observable <Spell> 1 2 3 4 Observable <Spell> 1 2

    4 LOSSY Debounce
  89. Observable <Spell> 1 2 3 4 Observable <Spell> 1 2

    4 PROBLEM SOLVED Debounce
  90. Problem : Optimize Network Traffic Track User Events using any

    analytics library.
  91. Problem : Optimize Network Traffic Track User Events using any

    analytics library. Simple Approach Server request whenever an event occurs. Over chatty producer (User Events) Slow Consumer (Network Request)
  92. Problem : Optimize Network Traffic Track User Events using any

    analytics library. Simple Approach Server request whenever an event occurs. Over chatty producer (User Events) Slow Consumer (Network Request) Backpressure Handling Required
  93. Problem : Optimize Network Traffic Track User Events using any

    analytics library. Buffer-undum Buffer some events before making a network request. Buffer-undum
  94. Problem : Optimize Network Traffic Track User Events using any

    analytics library. Buffer-undum getUserEventsObservable()
 .buffer(3) //buffer count
 .flatMap(new Function<List<Event>, Observable<TrackRequest>>() {
 @Override
 public Observable<TrackRequest> apply(final List<Event> events) {
 return Observable.just(new TrackRequest(events));
 }
 });
  95. Problem : Auto-Search AutoComplete Search Suggestions

  96. Problem : Auto-Search AutoComplete Search Suggestions Simple Approach Server request

    whenever a character is typed Over chatty producer (User Input) Slow Consumer (Network Request)
  97. Problem : Auto-Search AutoComplete Search Suggestions Simple Approach Server request

    whenever a character is typed Over chatty producer (User Input) Slow Consumer (Network Request) Backpressure Handling Required
  98. AutoComplete Search Suggestions Debounce-y Make a network request only if

    user has stopped typing. Debounce-y Solution : Auto-Search
  99. AutoComplete Search Suggestions Debounce-y getSearchTermObservable()
 .debounce(500, TimeUnit.MILLISECONDS)
 .flatMap(new Function<String, Observable<SearchRequest>>()

    {
 @Override
 public Observable<SearchRequest> apply(String term) {
 return Observable.just(new SearchRequest(term));
 }
 }); Solution : Auto-Search
  100. Error Handling “Muggle Art”

  101. “Repeat-ium” repeat() operator

  102. Repeat Emit the same item multiple times

  103. Repeat “Create an Observable that emits a particular item multiple

    times” - source Emit the same item multiple times
  104. Repeat-ium Observable <Integer> Repeat Observable <Integer> 1 1 1 1

    1 4
  105. “Retry-ikulus” retry() operator

  106. Retry On error, retry the same task until successfully completed.

  107. Retry “If a source Observable emits an error, resubscribe to

    it in the hopes that it will complete without error” - source On error, retry the same task until successfully completed.
  108. Retry Observable <Integer> 1 Observable <Integer> 1 2 3 2

    x S U B S C R I B E
  109. Retry-ikulus Retry Observable <Integer> 1 Observable <Integer> 1 2 3

    2 x S U B S C R I B E
  110. Retry Observable <Integer> 1 Observable <Integer> 1 2 3 2

    x 1 2 3 1 2 3 S U B S C R I B E S U B S C R I B E
  111. via-Hedwig repeat() resubscribes when it receives onCompleted(). retry() resubscribes when

    it receives onError(). 
  112. Fictional Problem (Professor Lupin’s Boggart) Perform a “Riddikulus” charm 1.

    Successfully 2. Excellently
  113. Fictional Problem (Professor Lupin’s Boggart) via-Hedwig “Riddikulus” charm is a

    magical spell to turn a Boggart  — your worst fear, into something funny Perform a “Riddikulus” charm 1. Successfully 2. Excellently
  114. Fictional Problem (Professor Lupin’s Boggart) Perform a “Riddikulus” charm 1.

    Successfully 2. Excellently SCARY FUNNY HILARIOUS Observable <BoggartState>
  115. Fictional Problem (Professor Lupin’s Boggart) SCARY FUNNY Successful Observable <BoggartState>

    Laughter Threshold 0 FUNNY_THRESHOLD Perform a “Riddikulus” charm 1. Successfully 2. Excellently
  116. Fictional Problem (Professor Lupin’s Boggart) Perform a “Riddikulus” charm 1.

    Successfully 2. Excellently SCARY FUNNY HILARIOUS Successful Excelled Observable <BoggartState> 0 FUNNY_THRESHOLD HILARIOUS_THRESHOLD Laughter Threshold
  117. Fictional Problem (Professor Lupin’s Boggart) via-Hedwig “Something is funny when

    it makes you laugh, but it becomes hilarious when it is extremely funny” . Perform a “Riddikulus” charm 1. Successfully 2. Excellently
  118. Fictional Problem (Professor Lupin’s Boggart) • Boggart has 3 states

    : SCARY, FUNNY and HILARIOUS. • Riddikulus Charm on Boggart, it’s state : Remain same (if it doesn’t cross the FUNNY_THRESHOLD), OR Change to FUNNY (if it crosses FUNNY_THRESHOLD) OR Change to HILARIOUS (if it crosses HILARIOUS_THRESHOLD). • We need to retry until the Boggart is atleast FUNNY. • We need to repeat until the Boggart becomes HILARIOUS.
  119. Solution SCARY SCARY S U B S C R I

    B E x Retry-kulus SCARY FUNNY S U B S C R I B E SCARY Repeat-ium HILARIOUS
  120. Solution getBoggartObservable()
 .flatMap(new Function<Boggart, Observable<Boggart>>() {
 @Override
 public Observable<Boggart> apply(Boggart

    boggart) {
 boggart.init();
 boggart.riddikulus();
 if (boggart.isFunny()) {
 return Observable.just(boggart);
 }
 return Observable.error(new BoggartException());
 }
 })
 .retry()
 .repeat(10)
 .takeUntil(new Function<Boggart, Boolean>() {
 @Override
 public Boolean apply(Boggart boggart) {
 return boggart.isHilarious();
 }
 });
  121. Problem : Session Renewal Keep session alive while user is

    browsing the app.
  122. Problem : Session Renewal Keep session alive while user is

    browsing the app. Simple Approach Checking for session validity before performing any API call.
  123. Problem : Session Renewal Keep session alive while user is

    browsing the app. Simple Approach Checking for session validity before performing any API call. Drawback Place session.isValid() check before each API call.
  124. Problem : Session Renewal Keep session alive while user is

    browsing the app. Another Approach Renew the token repeatedly while user is browsing the app.
  125. Problem : Session Renewal Keep session alive while user is

    browsing the app. Another Approach Renew the token repeatedly while user is browsing the app. Repeat-ium
  126. Solution : Session Renewal Observable<Session> sessionObservable = SessionManager.getCurrentSessionObservable()
 .filter(new Function<Session,

    Boolean>() {
 @Override
 public Boolean apply(Session session) {
 return session.isValid();
 }
 });
  127. Solution : Session Renewal Observable<Session> sessionObservable = SessionManager.getCurrentSessionObservable()
 .filter(new Function<Session,

    Boolean>() {
 @Override
 public Boolean call(Session session) {
 return session.isValid();
 }
 }); Observable<Session> renewSessionObservable =
 SessionManager.getCurrentSessionObservable()
 .flatMap(new Function<Session, Observable<Session>>() {
 @Override
 public Observable<Session> apply(Session session) {
 return authService.renewSession(session.getSessionId())
 .repeatWhen(new Function<Observable<? extends Void>, Observable<?>>() {
 @Override
 public Observable<?> apply(Observable<? extends Void> observable) {
 return observable.delay(Session.EXPIRY_TIME_IN_MILLIS,
 TimeUnit.MILLISECONDS);
 }
 });
 }
 });
  128. Solution : Session Renewal Observable<Session> sessionObservable = SessionManager.getCurrentSessionObservable()
 .filter(new Function<Session,

    Boolean>() {
 @Override
 public Boolean apply(Session session) {
 return session.isValid();
 }
 }); Observable<Session> renewSessionObservable =
 SessionManager.getCurrentSessionObservable()
 .flatMap(new Function<Session, Observable<Session>>() {
 @Override
 public Observable<Session> apply(Session session) {
 return authService.renewSession(session.getSessionId())
 .repeatWhen(new Function<Observable<? extends Void>, Observable<?>>() {
 @Override
 public Observable<?> apply(Observable<? extends Void> observable) {
 return observable.delay(Session.EXPIRY_TIME_IN_MILLIS,
 TimeUnit.MILLISECONDS);
 }
 });
 }
 }); sessionObservable.concatWith(renewSessionObservable);
  129. Demo “Multiple API calls”

  130. Coding Problem (Polyjuice Potion) • We are waiting for FluxWeed

    API call. • We are also waiting for Student API call to get hair of Crab. • Both the calls executing asynchronously. • Problem : • Initialise PolyJuice from the results of API calls. • Hide the loader after both calls have completed.
  131. Coding Problem (Polyjuice Potion) • We are waiting for FluxWeed

    API call. • We are also waiting for Student API call to get hair of Crab. • Both the calls executing asynchronously. • Problem : • Initialise PolyJuice from the results of API calls. • Hide the loader after both calls have completed. Without RxJava : SEMAPHORES!
  132. Zip-Yosa Observable <FluxWeed> Zip Observable <Polyjuice> FluxWeed CrabHair Polyjuice Potion

    Observable <CrabHair>
  133. • Learn by practice • Make RxJava your friend and

    not a foe • Learn by examples - Kaushik Gopal RxJava https://github.com/kaushikgopal/RxJava-Android-Samples • Learn more about RxJava 2 https://github.com/kaushikgopal/RxJava-Android-Samples • Have a look at the source code of demo app https://github.com/ragdroid/rxify • My Medium blogs for more details https://medium.com/@ragdroid What’s Next
  134. Acknowledgements via-Hedwig - Vikas @vickyturtle - Julien - Ritesh @_Riteshhh

    - DroidconIN team
  135. Acknowledgements via-Hedwig The terms lossy and lossless have been inspired

    from the post here.
  136. Acknowledgements via-Hedwig repeat() resubscribes when it receives onCompleted(). retry() resubscribes

    when it receives onError().  — Dan Lew
  137. Acknowledgements via-Hedwig “Something is funny when it makes you laugh,

    but it becomes hilarious when it is extremely funny” — source
  138. Acknowledgements via-Hedwig Technical Definitions have been taken from website.

  139. Acknowledgements via-Hedwig Fragmented Podcast here

  140. Acknowledgements via-Hedwig Gifs have been taken from giphy.com.

  141. Thank You

  142. FUELED http://fueled.com hello@fueled.com +1.800.962.4587 568 Broadway, 11th Flr. New York,

    NY 10012 Questions?