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

RxJava in practice

RxJava in practice

Slides for a workshop about RxJava & RxAndroid delivered at the Codemotion 2015.

Javier Gamarra

November 30, 2015
Tweet

More Decks by Javier Gamarra

Other Decks in Programming

Transcript

  1. MADRID · NOV 27-28 · 2015 Environment Eclipse | Android

    Studio (RxAndroid) RxJava.jar [Java 8] || [Retrolambda] :P
  2. MADRID · NOV 27-28 · 2015 Environment Android Studio: compile

    'io.reactivex:rxandroid:1.0.1' compile 'io.reactivex:rxjava:1.0.16' Eclipse: • Copy jars to lib/ and add jar in project
  3. MADRID · NOV 27-28 · 2015 Github • http://kcy.me/296bu •

    koans • código • commit a commit • Podéis preguntarme en cualquier momento
  4. MADRID · NOV 27-28 · 2015 Who? Javier Gamarra /

    @nhpatt @liferay @cyliconvalley / @agilespain
  5. MADRID · NOV 27-28 · 2015 Ask! Please? Pretty please?

    Please pretty please with sugar on top?
  6. MADRID · NOV 27-28 · 2015 Why? (in java) multithreading

    is hard futures are complicated callbacks are hell
  7. MADRID · NOV 27-28 · 2015 a library to represent

    any operation as an asynchronous data stream on any thread, declaratively composed, and consumed by multiple objects
  8. MADRID · NOV 27-28 · 2015 Libraries are tools If

    all you have is a hammer, everything looks like a nail
  9. MADRID · NOV 27-28 · 2015 Observables and Subscribers An

    Observable emits items. A Subscriber consumes those items.
  10. MADRID · NOV 27-28 · 2015 My very first observable

    Observable<String> myObs = Observable. create(new Observable. OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { subscriber.onNext( "Hi!"); subscriber.onCompleted(); } });
  11. MADRID · NOV 27-28 · 2015 My very first subscriber

    Subscriber<String> mySubs = new Subscriber<String>() { @Override public void onCompleted() { } @Override public void onError(Throwable throwable) { } @Override public void onNext(String s) { System. out.println(s); } };
  12. MADRID · NOV 27-28 · 2015 A bit verbose... Let’s

    simplify with Java8 and RxJava • lamdbas • Observable.just • .subscribe()
  13. MADRID · NOV 27-28 · 2015 Why? More than the

    observer pattern • Observables only emit when someone listening • OnFinished • OnError
  14. MADRID · NOV 27-28 · 2015 Let’s use those differences

    subscribe admits 3 parameters • OnNext • OnFinished • OnError
  15. MADRID · NOV 27-28 · 2015 Iterators? • Iterators are

    pull and synchronous • Subscribers are push and asynchronous
  16. MADRID · NOV 27-28 · 2015 to represent any operation

    as an “asynchronous data stream”
  17. MADRID · NOV 27-28 · 2015 Everything is a stream

    We can model everything as a stream
  18. MADRID · NOV 27-28 · 2015 Everything is a stream

    A network call is also a stream Like using retrofit with this API
  19. MADRID · NOV 27-28 · 2015 Map List<String> severalThings =

    Arrays.asList("1", "2"); Observable.from(severalThings) .map((s) -> “Element “ + s) .subscribe(System.out::println);
  20. MADRID · NOV 27-28 · 2015 Map We can return

    another type: List<String> severalThings = Arrays.asList("1", "2"); Observable.from(severalThings) .map(Integer::valueOf) .subscribe(System.out::println);
  21. MADRID · NOV 27-28 · 2015 Map Let’s do the

    same with our repos… And… I can’t because we return a List And using Observable.from… NOP
  22. MADRID · NOV 27-28 · 2015 Filter service.listRepos("nhpatt") .flatMap(Observable::from) .map(Repo::getName)

    .map((s) -> s.replace("-", " ")) .filter((s) -> s.startsWith("Android")) .subscribe(System.out::println);
  23. MADRID · NOV 27-28 · 2015 Scan & old code

    service.listRepos("nhpatt") .flatMap(Observable::from) .map(Repo::getName) .map((s) -> s.replace("-", " ")) .filter((s) -> s.startsWith("Android")) .take(2) .map(String::length) .scan((x, y) -> x * y) .subscribe(System.out::println);
  24. MADRID · NOV 27-28 · 2015 Scan & old code

    (l) -> { int result = 0; int i = 0; int oldLength = 1; for (Repo repo : l) { String name = repo.getName(); String replacedName = name.replace( "-", " "); if (replacedName.startsWith( "Android") && i < 2) { result = replacedName.length() * oldLength; oldLength = result; System.out.println(result); i++; } } return result;
  25. MADRID · NOV 27-28 · 2015 Operators • merge •

    flatMap • zip Nice things™ : parallel jobs!
  26. MADRID · NOV 27-28 · 2015 Zipping requests Observable<Repo> repo

    = service.listRepos("nhpatt") .flatMap(Observable::from) .take(1); Observable<Commit> commit = service.listCommits("nhpatt", "Android") .flatMap(Observable::from) .take(1); Observable.zip(repo, commit, this::updateCommit).subscribe (repo1 -> { System.out.println(repo1.getCommit().getUrl()); });
  27. MADRID · NOV 27-28 · 2015 Operators Amazing documentation •

    Async • Blocking • Combining • Conditional • Error handling • Filtering • Mathematical • Creation • String • Transforming • Utility
  28. MADRID · NOV 27-28 · 2015 Schedulers I define an

    API declaratively and later in the implementation run it: asynchronously or in a separate Thread or in a Threadpool or synchronously ...
  29. MADRID · NOV 27-28 · 2015 Use cases • Autocomplete

    with debounce | sample… • Accumulate calls with buffer... • Polling with timeout | window… • Form Validation with combineLatest… • Retrieve data fast from cache | network call with concat | merge… • Button click with delay, listen on other thread
  30. MADRID · NOV 27-28 · 2015 Why? (in android) Main/UI

    thread and background problem • Can’t do heavy tasks on UI • Can’t update view on background • AsyncTasks are bad • Handlers can leak
  31. MADRID · NOV 27-28 · 2015 Android schedulers service.listRepos("nhpatt") .subscribeOn(Schedulers.io())

    .observeOn(AndroidSchedulers.mainThread()) .subscribe(newRepos -> { repos.addAll(newRepos); adapter.notifyDataSetChanged(); });
  32. MADRID · NOV 27-28 · 2015 Orientation Problem Orientation Problem

    • onCreate gets called again • state is lost
  33. MADRID · NOV 27-28 · 2015 Orientation Problem Programmer’s job:

    • Store/Restore state • Restore view • Call again the user task? • Wait until it finishes?
  34. MADRID · NOV 27-28 · 2015 Orientation Problem Observable is

    easy to persist (retain | singleton) Observable can continue (cache and retry) RxLifecycle
  35. MADRID · NOV 27-28 · 2015 Orientation Problem Observable<List<Repo>> github

    = RetrofitService. getGithub() .listRepos("nhpatt") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .cache();
  36. MADRID · NOV 27-28 · 2015 But... Subscriptions leak memory

    :’( We have to call to unsubscribe (CompositeSubscription helps) And don’t include references to the object/activity
  37. MADRID · NOV 27-28 · 2015 Easy to unsubscribe @Override

    protected void onStop() { super.onStop(); subscription.unsubscribe(); } .isUnsubscribed() :(
  38. MADRID · NOV 27-28 · 2015 a library to represent

    any operation as an asynchronous data stream on any thread, declaratively composed, and consumed by multiple objects
  39. MADRID · NOV 27-28 · 2015 a library to represent

    any operation as an observable on any thread, declaratively composed, and consumed by multiple objects
  40. MADRID · NOV 27-28 · 2015 a library to represent

    any operation as an observable with schedulers declaratively composed, and consumed by multiple objects
  41. MADRID · NOV 27-28 · 2015 a library to represent

    any operation as an observable with schedulers and operators and consumed by multiple objects
  42. MADRID · NOV 27-28 · 2015 a library to represent

    any operation as an observable with schedulers and operators listened by subscribers
  43. MADRID · NOV 27-28 · 2015 But... • Learning curve

    • Hard to debug (Frodo library) • Backpressure
  44. MADRID · NOV 27-28 · 2015 Where to know more...

    • reactivex.io (amazing docs) • RxJava wiki • Dan Lew • Use cases
  45. MADRID · NOV 27-28 · 2015 Where to know more...

    nhpatt (twitter | github | email | slack) I’ll do anything for good votes
  46. MADRID · NOV 27-28 · 2015 Take service.listRepos("nhpatt") .flatMap(Observable::from) .map(Repo::getName)

    .map((s) -> s.replace("-", " ")) .filter((s) -> s.startsWith("Android")) .take(2) .subscribe(System.out::println);
  47. MADRID · NOV 27-28 · 2015 onError() is called if

    an Exception is thrown at any time (this is cool) The operators don't have to handle the Exception No more callbacks Errors
  48. MADRID · NOV 27-28 · 2015 Errors And we can

    customize the flow: onErrorResumeNext onErrorReturn retry ...
  49. MADRID · NOV 27-28 · 2015 Why this is useful?

    Observables and subscribers can do anything
  50. MADRID · NOV 27-28 · 2015 Why this is useful?

    Observable a database query Subscriber displaying results on the screen
  51. MADRID · NOV 27-28 · 2015 Why this is useful?

    Observable a click on the screen Subscriber reacting to it
  52. MADRID · NOV 27-28 · 2015 Why this is useful?

    Observable a stream of bytes from the internet Subscriber write them to disk
  53. MADRID · NOV 27-28 · 2015 Why this is useful?

    Observable and Subscriber are independent of the transformations
  54. MADRID · NOV 27-28 · 2015 Why this is useful?

    I can compose/chain any map/filter calls I want Only matters the return type
  55. MADRID · NOV 27-28 · 2015 Side effects doOn methods:

    • doOnNext() for debugging • doOnError() for error handling • doOnNext() to save/cache results
  56. MADRID · NOV 27-28 · 2015 Side effects Observable someObservable

    = Observable .from(Arrays.asList(new Integer[]{2, 7, 11})) .doOnNext(System.out::println) .filter(prime -> prime % 2 == 0) .doOnNext(System.out::println) .count() .doOnNext(System.out::println) .map( number -> String.format(“Contains %d elements”, number) );
  57. MADRID · NOV 27-28 · 2015 Side effects flatMap(id ->

    service.get() .doOnError(t -> { // report problem to UI }) .onErrorResumeNext(Observable.empty()))
  58. MADRID · NOV 27-28 · 2015 Cold & Hot Observables

    only emit when someone listening? Cold observables only emit when subscribed But hot observables emit instantly
  59. MADRID · NOV 27-28 · 2015 Cold & Hot You

    can convert between both states • “Hot -> cold” using defer() or merge(), zip()... • “Cold -> Hot” using publish() & connect()
  60. MADRID · NOV 27-28 · 2015 Cold & Hot publish

    & connect? “like transactions” Assume everything is cold
  61. MADRID · NOV 27-28 · 2015 More cool things? •

    testscheduler • toIterator() • Obs.error(), Obs.empty()
  62. MADRID · NOV 27-28 · 2015 Subjects AsyncSubject -> takeLast(1)

    PublishSubject -> publish() ReplaySubject -> replay()/cache() BehaviorSubject -> replay(1)/cache(1)