Functional Reactive Programming with RxJava on Android

What does it do?



  • Allows you to compose flows and sequences of asynchronous data
  • Allows you to create Observables and react when they change
  • Can act like a publish/subscribe system on steroids.

  
	Observable.from(1, 2, 3, 4, 5)
              .map(value -> value * 2)
              .subscribe(value -> Log.i(TAG, "" + value)); 

        Outputs:
        >> 2
        >> 4
        >> 6
        >> 8
        >> 10 

Imperative vs Declarative

     

     Imperative

 
  			 A = 0        // Store zero in A
			 B = A + 1    // Read A, add one to it, store in B
			 A = 2        // Store two in A
  			 Print A, B   // 2, 1
 	

     Declarative


			A = 0       // Emit value of 0 from observable A
			B = A + 1   // Set B to be A + 1
			A = 2       // Emit the value 2 from observable A
			Print A, B  // 2, 3
		

Credit: Timo Tuominen (https://github.com/tehmou/rx-android-architecture)

How the Magic Happens


    RxJava:  

    https://github.com/Netflix/RxJava

    RxJava-Android:  
      https://github.com/Netflix/RxJava/tree/master/rxjava-contrib/rxjava-android


    Retrolambda:

    https://github.com/evant/gradle-retrolambda

How the Magic Happens (cont.)


 
 
 buildscript {
    ...
    dependencies {
        classpath 'me.tatarka:gradle-retrolambda:2.1.0'
    }
 }
 

 apply plugin: 'com.android.application'
 apply plugin: 'retrolambda'
 
 
 android {
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_1_8
         targetCompatibility JavaVersion.VERSION_1_8
     }
 }
 
 dependencies {
     ...
     compile 'com.netflix.rxjava:rxjava-android:0.19.+'
 }
 

Observables


 public interface Observer<T> {
     public abstract void onCompleted();
     public abstract void onError(Throwable e);
     public abstract void onNext(T t);
 }

 public class Observable<T> {
     public final static <T> Observable<T> create(OnSubscribe<T> f)
     public final static <T> Observable<T> from(T t1)
     public final static <T> Observable<T> from(Iterable<? extends T> iterable)
     public final Subscription subscribe(final Action1<? super T> onNext)
}
 

Imgur Example


 public interface ImgurService {

    @GET("/gallery/{section}")
    Observable<ImgurGallery> getGallery(@Path("section") Section section);
}
 public class ImgurGallery {
    @SerializedName("data")
    List<ImgurImage> images;
 }
 public class ImgurImage {
    String  id;
    String  title;
    String  link;
    Integer ups;
    Integer downs;
    Integer score;
    ... getters/setters
}

Imgur Example (cont.)


public static class RetainedListFragment 
       extends ListFragment implements Observer<List<String>> {

  @Override
  public void onCompleted() {
      Toast.makeText(getActivity(), "onCompleted!", Toast.LENGTH_SHORT);
  }


  @Override
  public void onError(Throwable e) {
      Log.e(TAG, "onError", e);
  }
      
  @Override
  public void onNext(List<String> strings) {
      adapter.addAll(strings);
  }
}


Imgur Example (cont.)

 public static final String IMGUR_API_URL = "https://api.imgur.com/3"; 
 
 @Provides
 @Singleton
 RestAdapter provideImgurAdapter(Endpoint endpoint, Client client) {

     return new RestAdapter.Builder()
                .setClient(client)
                .setEndpoint(endpoint)
                .setRequestInterceptor(new ImgurAuthenticationInterceptor())
                .build();
    }

 @Provides @Singleton
 ImgurService provideGalleryService(RestAdapter restAdapter) {
     return restAdapter.create(ImgurService.class);
 }
 public class ImgurAuthenticationInterceptor implements RequestInterceptor {

    @Override
    public void intercept(RequestFacade request) {
        request.addHeader("Authorization", "Client-ID " + BuildConfig.IMGUR_CLIENT_ID);
    }
}

Imgur Example (cont)

 @Override
 public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   RxJavaApp.get(getActivity()).inject(this);

   adapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1);
   setListAdapter(adapter);
imgurService.getGallery(Section.HOT) .map(gallery -> gallery.getImages()) .flatMap(images -> Observable.from(images)) .filter(image -> (image.getUps() > 500)) .filter(image -> StringUtil.isNotEmpty(image.getTitle())) .flatMap(imgurImage -> Observable.from(imgurImage.getTitle())) .toList() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this); }

Imgur Example (cont)

The Map Operator


   imgurService.getGallery(Section.HOT)
               .map(gallery -> gallery.getImages())

Map - transform the items emitted by an Observable by applying a function to each of them

 .map(new Func1<ImgurGallery, List<ImgurImage>>() {
     @Override
     public List<ImgurImage> call(ImgurGallery gallery) {
         return gallery.getImages();
     }
  })

The FlatMap Operator


   imgurService.getGallery(Section.HOT)
               .map(gallery -> gallery.getImages())
               .flatMap(images -> Observable.from(images))

FlatMap - transform the items emitted by an Observable into Observables, then flatten this into a single Observable

 .flatMap(new Func1<List<ImgurImage>, Observable<? extends ImgurImage>>() {
    @Override
    public Observable<? extends ImgurImage> call(List<ImgurImage> images) {
        return Observable.from(images);
    }
 })

The Filter Operator

 imgurService.getGallery(Section.HOT)
        .map(gallery -> gallery.getImages())
        .flatMap(images -> Observable.from(images))
        .filter(image -> (image.getUps() > 500))

Filter - Remove elements that don't pass a test
 .filter(new Func1<ImgurImage, Boolean>() {
    @Override
    public Boolean call(ImgurImage image) {
        return (image.getUps() > 500);
    }
})

The Filter Operator Continued


    imgurService.getGallery(Section.HOT)
               .map(gallery -> gallery.getImages())
               .flatMap(images -> Observable.from(images))
               .filter(image -> (image.getUps() > 500))
               .filter(image -> StringUtil.isNotEmpty(image.getTitle()))

Replaced with anonymous class:
 .filter(new Func1<ImgurImage, Boolean>() {
    @Override
    public Boolean call(ImgurImage image) {
        return StringUtil.isNotEmpty(image.getTitle());
    }
 })

FlatMap Revisited


    imgurService.getGallery(Section.HOT)
               .map(gallery -> gallery.getImages())
               .flatMap(images -> Observable.from(images))
               .filter(image -> (image.getUps() > 500))
               .filter(image -> StringUtil.isNotEmpty(image.getTitle()))
               .flatMap(imgurImage -> Observable.from(imgurImage.getTitle()))

Transform our 'ImgurImage' into a single string
 .flatMap(new Func1<ImgurImage, Observable<? extends String>>() {
    @Override
    public Observable<? extends String> call(ImgurImage imgurImage) {
        return Observable.from(imgurImage.getTitle());
    }
 })

Combining Observables - toList

    imgurService.getGallery(Section.HOT)
               .map(gallery -> gallery.getImages())
               .flatMap(images -> Observable.from(images))
               .filter(image -> (image.getUps() > 500))
               .filter(image -> StringUtil.isNotEmpty(image.getTitle()))
               .flatMap(imgurImage -> Observable.from(imgurImage.getTitle()))
               .toList()


toList - collect all items from an Observable and emit them as a single List

toSortedList - Same as toList, but emits a sorted list

toMap - Converts the items to a Map (you provide a function that picks the keys)

reduce - Reduces all of the Observables to a single value

    

Putting it all Together

    imgurService.getGallery(Section.HOT)
               .map(gallery -> gallery.getImages())
               .flatMap(images -> Observable.from(images))
               .filter(image -> (image.getUps() > 500))
               .filter(image -> StringUtil.isNotEmpty(image.getTitle()))
               .flatMap(imgurImage -> Observable.from(imgurImage.getTitle()))
               .toList()
               .subscribeOn(Schedulers.io())
               .observeOn(AndroidSchedulers.mainThread())
               .subscribe(this);

  subscribeOn - What thread to run this on when subscribed.
  observeOn - The thread that onNext(), onCompleted(), and onError()       will be called on.
  subscribe - Subscribes to an Observable and invokes it


Links


Slides: https://slides.com/henrypate/rxjava/

Github: github.com/antew/RxJava-Examples

RxJava: https://github.com/Netflix/RxJava

RxJava

By Henry Pate

RxJava

  • 1,159