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

Saving_lives_with_RxJava.pdf

 Saving_lives_with_RxJava.pdf

Explore the basics of RxJava and walk through real world examples. You’ll learn how to create a simple solution for complex problems, saving you lines of code and making your code more readable.

Shahar Barsheshet

March 08, 2017
Tweet

Other Decks in Programming

Transcript

  1. Talk agenda • Intro to RxJava • Why and How?

    • Common Mistakes and some tips
  2. Intro to Rx Wikipedia: reactive programming is a programming paradigm

    oriented around data flows and the propagation of change. This means that it should be possible to express static or dynamic data flows with ease in the programming languages used, and that the underlying execution model will automatically propagate changes through the data flow. RxJava ← Reactive eXtentions ← Reactive Programing
  3. Intro to Rx Definition from ReactiveX: ReactiveX is a library

    for composing asynchronous and event-based programs by using observable sequences. It extends the observer pattern to support sequences of data and/or events and adds operators that allow you to compose sequences together declaratively while abstracting away concerns about things like low-level threading, synchronization, thread-safety, concurrent data structures, and non-blocking I/O.
  4. The Problem Some solutions: • AsyncTask ◦ Comfortable - nice

    callbacks ◦ Error handling sucks - Tuple?? ◦ The lost Context ◦ Composing multiple calls… oh no! ◦ Testing… yeah right… • Threading ◦ Very hard to handle concurrency ◦ Bugs are easy to create • IntentService ◦ Easy to use ◦ Updating the UI isn't trivial ◦ Concurrency…???
  5. The Solution RxJava • Can easily join background operations •

    Easy error handling • Can be tied to lifecycle • Easy to test • Remove boilerplate code
  6. RxJava with Android • RxAndroid - Android Schedulers • RxBinding

    - by Jake Wharton • RxJava compile 'com.jakewharton.rxbinding:rxbinding:1.0.0' compile 'io.reactivex:rxandroid:1.2.1' compile 'io.reactivex:rxjava:1.2.6' compile 'io.reactivex:rxjava:1.2.6'
  7. The Observable Observables emits data when you subscribe to them.

    • onNext(0 … ∞) • onComplete(0 … 1) • onError(0 … 1) onError() and onComplete() are exclusive.
  8. Subscribers and Subscriptions Once subscribing to an Observable, we get

    the Subscription object. The Subscription describes the connection between the Observable and the observer. As long as we are subscribed, we will get data emitted from the Observable. To stop the Observable from emitting more item, we just need to call subscription.unsubscribe();
  9. Error handling When an error occurs, the Observable stop emitting

    data. onError(Throwable t) gets called so we can handle the error in a proper way. What happens if we don't implement onError(Throwable t) ?
  10. The Magazine... Subscribing - creating the Subscription Preparing data Data

    ready - user can observe Error!!! Complete Observable Observer Unsubscribe
  11. Subscribers and Subscriptions Subscription mySubscription = myIntegers.subscribe(new Subscriber<Integer>() { @Override

    public void onCompleted() { // YEY!! finished emitting all the integers } @Override public void onError(Throwable e) { // something bad happened } @Override public void onNext(Integer integer) { // we just got a new integer } }); Observable<Integer> myIntegers = Observable.just(1, 2, 3, 4, 5); mySubscription.unsubscribe();
  12. Operators ReactiveX offers lots of operators to handle and manipulate

    data. Creating Observables • Just — convert an object or a set of objects into an Observable that emits that or those objects • Interval — create an Observable that emits a sequence of integers spaced by a particular time interval • Create — create an Observable from scratch by calling observer methods programmatically Transforming Observables • Map — transform the items emitted by an Observable by applying a function to each item Combining Observables • Merge — combine multiple Observables into one by merging their emissions • CombineLatest — when an item is emitted by either of two Observables, combine the latest item emitted by each Observable via a specified function and emit items based on the results of this function
  13. Operators Observable<Integer> myIntegers = Observable.just(1, 2, 3, 4, 5); Observable<Integer>

    evenIntegers = myIntegers.filter(new Func1<Integer, Boolean>() { @Override public Boolean call(Integer integer) { return integer % 2 == 0; } }); mappedToString.subscribe(); // onNext("Just mapped 2") // onNext("Just mapped 4") // onComplete() Observable<String> mappedToString = evenIntegers.map(new Func1<Integer, String>() { @Override public String call(Integer integer) { return "Just mapped " + integer; } });
  14. Operators Observable<Integer> myIntegers = Observable.just(1, 2, 3, 4, 5); myIntegers.filter(new

    Func1<Integer, Boolean>() { @Override public Boolean call(Integer integer) { return integer % 2 == 0; } }).map(new Func1<Integer, String>() { @Override public String call(Integer integer) { return "Just mapped " + integer; } });
  15. Lambda Expression new Func1<String, Integer>() { @Override public Integer call(String

    string) { return string.length(); } }; (String string) -> { return string.length(); }; string -> string.length(); = =
  16. Operators myIntegers .filter(integer -> integer % 2 == 0) .map(integer

    -> "Just mapped " + integer); Observable<Integer> myIntegers = Observable.just(1, 2, 3, 4, 5); myIntegers.filter(new Func1<Integer, Boolean>() { @Override public Boolean call(Integer integer) { return integer % 2 == 0; } }).map(new Func1<Integer, String>() { @Override public String call(Integer integer) { return "Just mapped " + integer; } }); From this: To this:
  17. Schedulers By default, an Observable and the chain of operators

    that you apply to it will do its work, and will notify its observers, on the same thread on which its Subscribe method is called. The SubscribeOn operator changes this behavior by specifying a different Scheduler on which the Observable should operate. • Schedulers.computation( ) meant for computational work such as event-loops and callback processing • Schedulers.io( ) meant for I/O-bound work such as asynchronous performance of blocking I/O, this scheduler is backed by a thread-pool that will grow as needed • AndroidSchedulers.mainThread( ) (using RxAndroid) subscribeOn() and observeOn
  18. AsyncTask String userId = "102"; AsyncTask getUserTask = new AsyncTask<String,

    Void, User>() { @Override protected User doInBackground(String[] strings) { return ServerAPI.getUserProfile(strings[0]); } @Override protected void onPostExecute(User user) { super.onPostExecute(user); mAdapter.add(user.firstName + " " + user.lastName); } }; getUserTask.execute(userId);
  19. AsyncTask String userId = "102"; Observable.just(userId) .map(id -> ServerAPI.getUserProfile(id)) .subscribe(user

    -> { mAdapter.add(user.firstName + " " + user.lastName); }, throwable -> { Toast.makeText(this, "An error occurred, please try again" , Toast.LENGTH_SHORT).show(); }); Observable.just(userId) .map(id -> ServerAPI.getUserProfile(id)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(user -> { mAdapter.add(user.firstName + " " + user.lastName); }, throwable -> { Toast.makeText(this, "An error occurred, please try again" , Toast.LENGTH_SHORT).show(); });
  20. TimerTask //initialize the TimerTask's job TimerTask timerTask = new TimerTask()

    { @Override public void run() { // Do some cool stuff // on the UI Thread } }; //set a new Timer Timer timer = new Timer(); //schedule the timer, after the first 5000ms // the TimerTask will run every 10000ms timer.schedule(timerTask, 5000, 10000);
  21. TimerTask Observable.interval(5000, 10000, TimeUnit.MILLISECONDS) .observeOn(AndroidSchedulers.mainThread()) .subscribe(interval -> { // Do

    some cool stuff // on the UI Thread }); public static Observable<Long> interval(long initialDelay, long period, TimeUnit unit) { return interval(initialDelay, period, unit, Schedulers.computation()); }
  22. Search address using Google Places API mEditText.addTextChangedListener(new TextWatcher() { @Override

    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { GetPlaces task = new GetPlaces(); String check = mEditText.getText().toString(); task.execute(check); } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {} @Override public void afterTextChanged(Editable editable) {} }); // get the list of predictions in an asynctask class GetPlaces extends AsyncTask<String, Void, ArrayList<String>> { @Override protected ArrayList<String> doInBackground(String... args) { ArrayList<String> predictionsArr = new ArrayList<String>(); try { ServerAPI.getSuggestionsFromGoogleAPI(args[0]); } catch (Exception e) { e.printStackTrace(); } // return all the predictions based on the typing the in the search return predictionsArr; } @Override protected void onPostExecute(ArrayList<String> result) { // update the list with new data } }
  23. Search address using Google Places API - using RxJava RxTextView.textChanges(mEditText)

    .debounce(300, TimeUnit.MILLISECONDS) .distinctUntilChanged() .filter(charSequence -> charSequence != null && charSequence.length() > 0) .map(charSequence -> ServerAPI.getSuggestionsFromGoogleAPI(charSequence)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnError(throwable -> { // show user something about this error }) .subscribe(strings -> { // update the list with new data });
  24. Getting the current country public Observable<CountryProvider> getCountryCode() { Observable<CountryProvider> locationObservable

    = getCountryFromLocationProvider(); Observable<CountryProvider> ipObservable = getCountryFromNetwork(); Observable<CountryProvider> providerObservable = getCountryFromNetworkProvider(); return Observable.merge(providerObservable, locationObservable, ipObservable) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .startWith(new CountryProvider("il", CountryProviderType.DEFAULT)) .scan((countryProvider, countryProvider2) -> { if (countryProvider2 == null) { return countryProvider; } int value1 = countryProvider.getProviderType().getValue(); int value2 = countryProvider2.getProviderType().getValue(); return value1 > value2 ? countryProvider : countryProvider2; }) .distinctUntilChanged() .onErrorReturn(null); }
  25. Getting the current country mCountryManager.getCountryCode() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(countryProvider -> {

    Toast.makeText(this, "Got country " + countryProvider.getCountryCode() + " from provider: " + countryProvider.getProviderType().name() , Toast.LENGTH_LONG).show(); });
  26. Default Schedulers Observable.just("hello") .subscribe(); → happens on the current thread

    Observable.just("hello") .delay(1000, TimeUnit.MILLISECONDS) .subscribe(); → happens on the computation thread VS
  27. ObserveOn Observable.just("Hello") // current thread .observeOn(Schedulers.io()) .map(string -> string.length()) //

    io .observeOn(Schedulers.computation()) .subscribe(); // computation Multiple observeOn do work as expected.
  28. CompositeSubscription Subscription myIntegerSubscription = myIntegers.subscribe(new Subscriber<Integer>() { @Override public void

    onCompleted() {} @Override public void onError(Throwable e) {} @Override public void onNext(Integer integer) {} }); Subscription myStringSubscription = myStrings.subscribe(new Subscriber<String>() { @Override public void onCompleted() {} @Override public void onError(Throwable e) {} @Override public void onNext(String string) {} }); compositeSubscription.add(myIntegerSubscription); compositeSubscription.add(myStringSubscription); Observable<Integer> myIntegers = Observable.just(1, 2, 3, 4, 5); Observable<String> myStrings = Observable.just("one", "two", "three", "four", "five"); compositeSubscription.unsubscribe(); // OR compositeSubscription.clear(); CompositeSubscription compositeSubscription = new CompositeSubscription();
  29. LifeCycle private CompositeSubscription mSubscriptions; @Override public void onViewCreated(View view, @Nullable

    Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mSubscriptions = new CompositeSubscription(); } @Override public void onDestroyView() { super.onDestroyView(); if (!mSubscriptions.isUnsubscribed()) mSubscriptions.unsubscribe(); } public void addSubscriptionToViewLifeCycle(Subscription subscription) { mSubscriptions.add(subscription); }