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()))
.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()))
.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
