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

Rxify - a simple spell for complex RxJava operators

Rxify - a simple spell for complex RxJava operators

Droidcon Boston 2017
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

April 11, 2017
Tweet

More Decks by ragdroid

Other Decks in Programming

Transcript

  1. #DroidConBos @ragdroid `Rxify` is for? • Already working on RxJava

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

    - More to learn. • Beginner - Time to start Rxifying your apps • Pros - Talk will be fun!
  3. #DroidConBos @ragdroid `Rxify` is for? • Already working on RxJava

    - More to learn. • Beginner - Time to start Rxifying your apps • Pros - Talk will be fun! • Not Interested?
  4. #DroidConBos @ragdroid • `Rxify` - term signifies use of RxJava

    • RxJava makes our lives simpler. • Cast a spell (use an operator) and we are good to go. `Rxify`
  5. #DroidConBos @ragdroid • Reactive Extensions for JVM (Java Virtual Machine).

    • Observer pattern for sequences of data / events. RxJava
  6. #DroidConBos @ragdroid RxJava makes lives simpler • Never having to

    worry about : • Low-level Threading • Synchronisation • Background Processing
  7. #DroidConBos @ragdroid RxJava operators at first glance • Complex •

    Hard to understand • Marble Diagrams? What!!
  8. The Plan • Basics • Spells ✦ map() ✦ flatmap()

    ✦ filter() • Complex Spells • Thread Travel
  9. The Plan • Basics • Spells • Complex Spells ✦

    zip() ✦ concat() vs merge() ✦ buffer() ✦ debounce() ✦ flatmap() vs concatMap() vs switchMap() • Thread Travel
  10. The Plan • Basics • Spells • Complex Spells •

    Thread Travel ✦ subscribeOn() ✦ observeOn()
  11. #DroidConBos @ragdroid • Observable produces items, • Subscriber consumes those

    items. • Observable calls : (Actions) • onNext() multiple times • followed by onComplete() • OR onError() Basics
  12. #DroidConBos @ragdroid • Operators : • manipulate items between producer

    and consumer. • Schedulers • Main Thread • Computation, IO (Background Thread) • Test Scheduler Basics
  13. #DroidConBos @ragdroid A B C A B C Rx-ify String

    Array Simple Observable List<String> source = Arrays.asList("A", "B", "C");
 Observable.fromIterable(source); Observable <String>
  14. #DroidConBos @ragdroid FlatMap • Every element of a collection •

    To another collection of items • Combines them into a single collection.
  15. #DroidConBos @ragdroid Observable <String> FlatMap List<String> source = Arrays.asList("Hello World",

    "How Are You");
 Observable.fromIterable(source)
 .flatMap(s -> Observable.fromIterable(
 Arrays.asList(s.split(" ")))); Observable <String> Hello World How Are You? Hello World How Are You?
  16. #DroidConBos @ragdroid Observable <Integer> Filter Observable <Integer> 1 2 3

    1 4 5 6 3 5 getSourceObservable() .filter(integer -> integer % 2 == 1) 7 7
  17. #DroidConBos @ragdroid Zip • Combine multiple collections together • According

    to some function • Emit the result of each combination.
  18. #DroidConBos @ragdroid 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)
  19. #DroidConBos @ragdroid Observable <FluxWeed> Zip Observable <Polyjuice> FluxWeed CrabHair Polyjuice

    Potion Observable <CrabHair> 
 return Observable.zip( fluxWeedObservable, hairObservable,
 (fluxWeed, student) -> new PolyJuice(fluxWeed, student.getHair()) .prepare()) );
  20. #DroidConBos @ragdroid Observable <FluxWeed> Zip Observable <Polyjuice> FluxWeed CrabHair Polyjuice

    Potion Observable <CrabHair> 
 return Observable.zip( fluxWeedObservable, hairObservable,
 (fluxWeed, student) -> new PolyJuice(fluxWeed, student.getHair()) .prepare()) );
  21. #DroidConBos @ragdroid Observable <FluxWeed> Zip Observable <Polyjuice> FluxWeed CrabHair Polyjuice

    Potion Observable <CrabHair> 
 return Observable.zip( fluxWeedObservable, hairObservable,
 (fluxWeed, student) -> new PolyJuice(fluxWeed, student.getHair()) .prepare()) );
  22. #DroidConBos @ragdroid Observable <FluxWeed> Zip Observable <Polyjuice> FluxWeed CrabHair Polyjuice

    Potion Observable <CrabHair> 
 return Observable.zip( fluxWeedObservable, hairObservable,
 (fluxWeed, student) -> new PolyJuice(fluxWeed, student.getHair()) .prepare()) );
  23. #DroidConBos @ragdroid Can you relate the Polyjuice Potion Problem to

    any coding problem? YES! When multiple API calls are executing simultaneously.
  24. #DroidConBos @ragdroid Concat “Emit the emissions from two or more

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

    H1 H2 Observable <Ravenclaw> Observable <Hufflepuff> G1 G2 S1 S2 Observable <Slytherin>
  28. #DroidConBos @ragdroid 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
  29. #DroidConBos @ragdroid 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>
  30. #DroidConBos @ragdroid 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
  31. #DroidConBos @ragdroid 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>
  32. #DroidConBos @ragdroid • Hit the cache first • If data

    found return data • Else Network call Problem : Cache First Then Network
  33. #DroidConBos @ragdroid • Hit the cache first • If data

    found return data • Else Network call Problem : Cache First Then Network Concat-ify
  34. #DroidConBos @ragdroid • 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
  35. #DroidConBos @ragdroid 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).
  36. #DroidConBos @ragdroid Buffer • Buffer small chunks of items •

    Instead of emitting one item at a time. • Emit chunks
  37. #DroidConBos @ragdroid Observable <Spell> Buffer Observable <Spell> 1 2 3

    4 1 2 getSpellsObservable()
 .buffer(2); //count 3 4
  38. #DroidConBos @ragdroid Debounce Emit an item only when, some time

    has passed since it last emitted anything.
  39. #DroidConBos @ragdroid Observable <Spell> 1 2 3 4 Observable <Spell>

    1 2 4 getSpellsObservable()
 .debounce(300, TimeUnit.MILLISECONDS); Debounce
  40. #DroidConBos @ragdroid 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)
  41. #DroidConBos @ragdroid Problem : Optimize Network Traffic Track User Events

    using any analytics library. Buffer-undum Buffer some events before making a network request. Buffer-undum
  42. #DroidConBos @ragdroid Problem : Optimize Network Traffic Track User Events

    using any analytics library. Buffer-undum Observable.just(new ArrayList<Event>())
 .buffer(3) // buffer count
 .flatMap(events -> Observable.just(new TrackRequest(events)));
  43. #DroidConBos @ragdroid Problem : Auto-Search AutoComplete Search Suggestions Simple Approach

    Server request whenever a character is typed Over chatty producer (User Input) Slow Consumer (Network Request)
  44. #DroidConBos @ragdroid AutoComplete Search Suggestions Debounce-y Make a network request

    only if user has stopped typing. Debounce-y Solution : Auto-Search
  45. #DroidConBos @ragdroid Solution : Subject Producer Consumer A B C

    items Observable emits Subscriber consumes
  46. #DroidConBos @ragdroid Solution : Subject Producer Consumer A B C

    items Subject emits Subscriber consumes subject.onNext()
  47. #DroidConBos @ragdroid Solution : Subject Producer Consumer A B C

    items Subject emits Subject consumes subject.onNext() subject.subscribe()
  48. #DroidConBos @ragdroid Solution : Subject Subject<T> extends Observable<T> implements Observer<T>

    { } via-Hedwig We are going to use a PublishSubject for this purpose.
  49. #DroidConBos @ragdroid Solution : Subject Subject<T> extends Observable<T> implements Observer<T>

    { } via-Hedwig PublishSubject : once you subscribe, you get all the subsequent items.
  50. #DroidConBos @ragdroid AutoComplete Search Suggestions Debounce-y publishSubject
 .debounce(300, TimeUnit.MILLISECONDS)
 .flatMap(new

    Function<String, ObservableSource<List<Book>>>() {
 @Override
 public ObservableSource<List<Book>> apply(String searchTerm) {
 return dataSource.getBook(searchTerm);
 }
 }) Solution : Auto-Search
  51. #DroidConBos @ragdroid AutoComplete Search Suggestions Debounce-y Solution : Auto-Search publishSubject


    .debounce(300, TimeUnit.MILLISECONDS)
 .flatMap(new Function<String, ObservableSource<List<Book>>>() {
 @Override
 public ObservableSource<List<Book>> apply(String searchTerm) {
 return dataSource.getBook(searchTerm);
 }
 })
  52. #DroidConBos @ragdroid Observable <List<Book>> Observable <String> B Solution : Auto-Search

    (FlatMap) What if Response (“Book) arrived before Response(“B”)? Book
  53. #DroidConBos @ragdroid Observable <List<Book>> List1 concatMap-um Observable <String> B Book

    Solution : Auto-Search (ConcatMap) 1000ms 200ms S U B S C R I B E
  54. #DroidConBos @ragdroid SwitchMap • Just like flatMap(), but … •

    Unsubscribe to previous observable • When new item emitted
  55. #DroidConBos @ragdroid Schedulers • Schedulers.computation() - for computational work •

    Schedulers.io() - for IO-bound work • AndroidSchedulers.mainThread() - executes actions on Android main thread • new TestScheduler() - intended for testing purposes
  56. #DroidConBos @ragdroid • A new Thread is like a parallel

    universe • We all live in the main thread • Switching a thread - opening a portal to another dimension Fictional Threading
  57. #DroidConBos @ragdroid • Hermione is always eager to gain knowledge.

    • Two lectures are going on at the same time. • She is stuck in the dilemma of how to attend both the lectures. Fictional Problem (Hermione’s Time Turner)
  58. #DroidConBos @ragdroid • Hermione is always eager to gain knowledge.

    • Two lectures are going on at the same time. • She is stuck in the dilemma of how to attend both the lectures. Fictional Problem (Hermione’s Time Turner) via-Hedwig Time Turner allows hermione travel back in time
  59. #DroidConBos @ragdroid ObserveOn • True magic of thread travel •

    Switch threads via-Hedwig Potterly speaking, observe-ate can open portals to different dimensions
  60. #DroidConBos @ragdroid Hermione’s Time Turner : Solution Observable.defer(new Callable<ObservableSource<Lecture>>() {


    @Override
 public ObservableSource<Lecture> call() {
 return Observable.just(new Lecture(id)) .delay(id, TimeUnit.SECONDS);
 }
 }); getLectureObservable()
  61. #DroidConBos @ragdroid Hermione’s Time Turner : Solution lectureOneObservable = getLectureObservable(1)


    .subscribeOn(AndroidSchedulers.mainThread())
 .observeOn(AndroidSchedulers.mainThread())
 .doOnNext(lecture -> {
 if (getView() != null) {
 getView().append("Attended Lecture " + lecture.getId());
 }
 });
  62. #DroidConBos @ragdroid Hermione’s Time Turner : Solution lectureTwoObservable = getLectureObservable(2)


    .subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThread())
 .doOnNext(lecture -> {
 if (getView() != null) {
 getView().append("Attended Lecture " + lecture.getId());
 }
 });
  63. #DroidConBos @ragdroid Hermione’s Time Turner : Solution 
 Observable.zip(lectureOneObservable, lectureTwoObservable,


    (lecture, lecture2) -> String.format("Attended Lectures %s and %s", lecture.getId(), lecture2.getId()))
 .subscribe(message -> {
 if (getView() != null) {
 getView().append(message);
 }
 }); Zip the two
  64. #DroidConBos @ragdroid • Learn by practice • Make RxJava your

    friend and not a foe • Learn by examples - Kaushik Gopal RxJava https://github.com/kaushikgopal/RxJava-Android-Samples • Have a look at the source code of demo app https://github.com/ragdroid/rxify • Slides at speakerdeck https://speakerdeck.com/ragdroid/rxify-a-simple-spell-for-complex-rxjava-operators-1 • My Medium blogs for more details https://medium.com/@ragdroid What’s Next
  65. #DroidConBos @ragdroid Acknowledgements via-Hedwig - Android community - Vikas @vickyturtle

    - Julien - Fueled - Ritesh @_Riteshhh - Droidcon Boston team
  66. #DroidConBos @ragdroid Acknowledgements via-Hedwig Response on medium article from Paul

    Woitaschek : https://medium.com/@woitaschek/its-better-to-use-switchmap- as-you-don-t-care-about-old-results-315029110c75