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

Rxify - a simple spell for complex RxJava operators

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

ragdroid

November 10, 2016
Tweet

More Decks by ragdroid

Other Decks in Programming

Transcript

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

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

    to learn. • Beginner - Time to start Rxifying your apps • Pros - Talk will be fun!
  3. • `Rxify` - term signifies use of RxJava • RxJava

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

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

    : • Low-level Threading • Synchronisation • Background Processing
  6. RxJava operators at first glance • Complex • Hard to

    understand • Marble Diagrams? What!!
  7. • Observable produces items, • Subscriber consumes those items. •

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

    • Schedulers • Main Thread • Computation (Background Thread) • Test Scheduler Basics
  9. A B C A B C Rx-ify String Array Simple

    Observable Observable.from(new String[] { "A", "B", “C"}); Observable <String>
  10. Map “Transform the items emitted by an Observable by applying

    a function to each item” - source Transforms every element of a collection of items.
  11. 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
  12. Observable <Integer> Map Observable <Integer> 1 2 3 10 20

    30 via-Hedwig In RxJava 2 Func1 has become Function Func2 has become BiFunction
  13. 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
  14. FlatMap Transforms every element of a collection into another collection

    of items and combines them into a single collection.
  15. 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.
  16. 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?
  17. 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.
  18. 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
  19. 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
  20. 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)
  21. 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();
 }
 });
  22. Can you relate the Polyjuice Portion Problem to any coding

    problem? YES! When multiple API calls are executing simultaneously.
  23. Concat “Emit the emissions from two or more Observables without

    interleaving them” source “Combine multiple Observables into one by merging their emissions” source Merge
  24. 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
  25. 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)).
  26. Observable <Gryffindor> Snape’s Assignment - Submission R1 R2 H1 H2

    Observable <Ravenclaw> Observable <Hufflepuff> G1 G2 S1 S2 Observable <Slytherin>
  27. 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
  28. 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>
  29. 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
  30. 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>
  31. • Hit the cache first • If data found return

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

    data • Else Network call Problem : Cache First Then Network Concat-ify
  33. • 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
  34. 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
  35. 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
  36. 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).
  37. 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
  38. Observable <Spell> Buffer Observable <Spell> 1 2 3 4 1

    2 getSpellsObservable()
 .buffer(2); //count 3 4
  39. Debounce From a list of items emit an item only

    when, some time has passed since it last emitted anything.
  40. 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
  41. Observable <Spell> 1 2 3 4 Observable <Spell> 1 2

    4 getSpellsObservable()
 .debounce(300, TimeUnit.MILLISECONDS); Debounce
  42. 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)
  43. 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
  44. Problem : Optimize Network Traffic Track User Events using any

    analytics library. Buffer-undum Buffer some events before making a network request. Buffer-undum
  45. 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));
 }
 });
  46. Problem : Auto-Search AutoComplete Search Suggestions Simple Approach Server request

    whenever a character is typed Over chatty producer (User Input) Slow Consumer (Network Request)
  47. 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
  48. AutoComplete Search Suggestions Debounce-y Make a network request only if

    user has stopped typing. Debounce-y Solution : Auto-Search
  49. 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
  50. Repeat “Create an Observable that emits a particular item multiple

    times” - source Emit the same item multiple times
  51. 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.
  52. 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
  53. 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
  54. Fictional Problem (Professor Lupin’s Boggart) Perform a “Riddikulus” charm 1.

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

    Laughter Threshold 0 FUNNY_THRESHOLD Perform a “Riddikulus” charm 1. Successfully 2. Excellently
  56. 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
  57. 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
  58. 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.
  59. 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
  60. 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();
 }
 });
  61. Problem : Session Renewal Keep session alive while user is

    browsing the app. Simple Approach Checking for session validity before performing any API call.
  62. 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.
  63. Problem : Session Renewal Keep session alive while user is

    browsing the app. Another Approach Renew the token repeatedly while user is browsing the app.
  64. 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
  65. Solution : Session Renewal Observable<Session> sessionObservable = SessionManager.getCurrentSessionObservable()
 .filter(new Function<Session,

    Boolean>() {
 @Override
 public Boolean apply(Session session) {
 return session.isValid();
 }
 });
  66. 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);
 }
 });
 }
 });
  67. 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);
  68. 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.
  69. 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!
  70. • 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
  71. Acknowledgements via-Hedwig “Something is funny when it makes you laugh,

    but it becomes hilarious when it is extremely funny” — source