UP | HOME
2017-02-22 | ../../ |

App Develop: Reactive Programming

Table of Contents

1 Abstract

Reactive programming is an extension of the Observer Software Design Pattern. It involves Observables (java.util.Observable) that publish values, and Observers (java.util.Observer) that watch and react. Reactive programming deals with asynchronous data streams.

2 Source Code of Examples

Build the Android APK with the src code rxandroidsample as you read these notes. Each concept is presented as a separate Activity: Ex #1, #2, #3

Include the following lines in your app build.gradle file.

dependencies {
    ...
    compile 'io.reactivex:rxandroid:1.2.1'
    compile 'io.reactivex:rxjava:1.1.9'
}

3 Ex#1: Hello World, Once Again

We will create myObservable, that retrieves a string from a TextView, and publishes it to an Observer. This Observer then outputs the string to a TextBlock.

Observable <String>  myObservable = Observable.create(
        new Observable.OnSubscribe <String> () {
            @Override
            public void call(Subscriber <? super String>  sub) {
                sub.onNext(rx1Binding.editText.getText().toString());
                sub.onCompleted();
            }
        }
);

A Subscriber is a class that implements the Observer interface.

3.1 Creating an Observer

The Observer Interface names three methods.

  1. onCompleted(): Called exactly once, when the Observable completes execution.
  2. onError(): Called exactly once, when an error occurs on the Observable.
  3. onNext(): The Observable calls this method whenever it wishes to publish a new value. Any number of times.
Observer myObserver = new Observer <String> () {

    @Override
    public void onCompleted() {
      // empty for now
    }

    @Override
    public void onError(Throwable e) {
      // empty for now
    }

    @Override
    public void onNext(String text) {
        rx1Binding.textView.setText(text);
    }
};

3.2 Linking Observable and Observer

rx1Binding.button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        myObservable.subscribe(myObserver);
    }
});

rxsample_activity1.png

Voila! We have used reactive programming to fetch and display a string in an Android app. Now, this is a lot of work to display a string, and can be done with much more simplicity and elegance using

rx1Binding.textView.setText(rx1Binding.editText.getText().toString());

4 Ex#2: Asynchronous Example

Suppose the Observable would need to perform some elapsed-time consuming task, such as fetching data from a remote server. We simulate this by pausing for 3 seconds while in the Observable, and then modify the received string before publishing it to the Observer.

rxsample_activity2.gif

Execution is paused for 3000 milliseconds. Content of the editText is split based on newlines. Each line is numbered. The builder is published to the Observer.

myObservable = Observable.create (
     new Observable.OnSubscribe <String> () {
         @Override
         public void call(Subscriber <? super String>  sub) {
             try {
                 Thread.sleep(3000);
             } catch (Exception e) {e.printStackTrace();}
             String[] strings =
                rx2Binding.editText.getText().toString().split("\n");
             StringBuilder builder = new StringBuilder();
             for (int i = 0; i  < strings.length; i++) {
                 builder.append((i+1) + ". " + strings[i] + "\n");
             }
             sub.onNext(builder.toString());
             sub.onCompleted();
         }
     });

The Observer remains unchanged from the previous sample. It expects a string from the Observable, and publishes this string to the TextBlock.

4.1 Execute Observable on Another Thread

In Android development, time consuming tasks are moved into an AsyncTask, and and not wait until it is completed. When the task is complete, theĀ UI is updated. To achieve the same using RxAndroid,

rx2Binding.button.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {
    rx2Binding.button.setEnabled(false);
    myObservable
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(myObserver);
  }
});

The subscribeOn() names the IO Scheduler thread that myObservable runs on. The observeOn() method names the thread the Observer should execute on. By default, the Observer will be executed on the same thread as the Observable; here we set it to the AndroidSchedulers.mainThread() [the Android UI thread].

4.2 Introducing Operators

Re-examine the code we used to subscribe in the previous step. TBD We added a couple of intermediate steps between the Observable and the final Observer. Each step above creates a new Observable, and there can be any number of intermediate steps between the initial Observable and the final Observer. These intermediate methods are referred to as operators, which should be a separate topic.

An interesting, and easy to understand operator is the map() operator. It transforms objects of one type to another. [Unrelated to map + reduce.]

map(new Func1 <Integer, String> () { 
        @Override 
        public String call(Integer integer) { 
                return String.valueOf(integer);
        } 
});

In the snippet above, we are converting an Observable that publishes an Integer into an Observable that publishes a String. In the call method, we receive an Integer, and return the String equivalent. The map() method handles the creation of the relevant Observable.

5 Ex#3: Publish a List

Our third example will expand on the second. We will split a string into a list. For each item in the list, we would append it's position in the list, and publish.

rxsample_activity3.gif

Observable.from(
  rx3Binding
   .editText
   .getText()
   .toString()
   .split("\n"))
     .subscribeOn(Schedulers.io())
     .map(new Func1 <String, String> () {
        @Override
        public String call(String morphedString) {
                      try {
                          Thread.sleep(2000);
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                      return mCounter++ + ". " + morphedString;
                  }
      })
         .observeOn(AndroidSchedulers.mainThread())
         .subscribe(myObserver);

The from() is a constructor. We have condensed the creation of the Observable, using Observable.from(). For each String published to map(), the app sleeps for 2000 milliseconds, appends the string position to the string, before finally publishing to the Observer. The Observer remains as before.

6 Handling Errors

An error happening at an Observable and its final Observer propagates to the Observer's onError() method. No matter how complex your Observable tree gets, the error handling is done at the end.

myObserver = new Observer <String> () {

    @Override
    public void onCompleted() {
        rx3Binding.button.setEnabled(true);
    }

    @Override
    public void onError(Throwable e) {
        Toast.makeText(SampleRxActivity4.this,
                "A \"" + e.getMessage() + "\" Error has been caught",
                Toast.LENGTH_LONG).show();
        rx4Binding.startButton.setEnabled(true);
        rx4Binding.stopButton.setEnabled(false);
    }

    @Override
    public void onNext(String string) {
        rx4Binding.textView
          .setText(rx4Binding.textView.getText() + "\n" + string);
    }
};

rxsample_error.gif

To test this, we included a toggle button, to indicate if we want to trigger an error condition. Within the map() operator, we attempt a divide by zero if the toggle is checked, as shown below.

Observable.from(rx4Binding.editText.getText().toString().split("\n"))
    .subscribeOn(Schedulers.io())
    .map(new Func1() {
            @Override
                public String call(String morphedString) {
                try {Thread.sleep(2000);} catch (Exception e) {e.printStackTrace();}
                if (rx4Binding.errorToggle.isChecked())
                    mCounter = 2 / 0;
                return mCounter++ + ". " + morphedString;
            }
        })
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(myObserver);

7 Stopping Subscriptions

So far, we have gone by the assumption that our Observables will execute to completion, and publish all values to the Observer. This is not realistic, since the user might close your app before your Observable has done executing. Also, your Observable possibly does never terminate, publishing values on a timer, or on receipt of data from some other source.

The subscribe() method returns a Subscription object, whose sole purpose is to allow unsubscribing.

public class SampleRxActivity4 extends AppCompatActivity {
    Subscription subscription;
    ...
}

Then we assign the Subscription created by calling subscribe to our Subscription variable.

subscription = Observable.from(
   rx4Binding.editText.getText().toString().split("\n"))
        .subscribeOn(Schedulers.io())
        ...

        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(myObserver);

And finally we unsubscribe whenever the Stop Subscription button is clicked.

rx4Binding.stopButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if (!subscription.isUnsubscribed())
            subscription.unsubscribe();
        rx4Binding.startButton.setEnabled(true);
        rx4Binding.stopButton.setEnabled(false);
    }
});

rxsample_stop_subscription.gif

Also, make sure to unsubscribe from all Observables in your Activity onStop() method (or onPause() depending on what you are observing).

@Override
public void onStop() {
    super.onStop();
    if (!subscription.isUnsubscribed())
        subscription.unsubscribe();
}

8 Final Notes

Observers should be what reacts to mutations. You should avoid performing any CPU and/or network intensive tasks on an Observer. All the hard work should be done in the Observable

9 References

  1. The above is a revised version of an article from Android Authority.
  2. RxJava and RxAndroid javadocs http://reactivex.io/RxJava/javadoc/rx/Observer.html http://reactivex.io/RxJava/javadoc/rx/Observable.html
  3. https://docs.oracle.com/javase/8/docs/api/java/util/Observable.html
  4. https://gist.github.com/staltz/868e7e9bc2a7b8c Title: The introduction to Reactive Programming you've been missing
  5. https://github.com/google/agera Agera is a set of classes and interfaces to help write functional, asynchronous, and reactive applications for Android.

10 End


www.wright.edu/~pmateti 2017-02-22