Rx
Android
Dev


About me
Alejandro Vidal Rodriguez
Director of Engineer @TribalScale

www.tribalscale.ae
we are hiring!
Agenda
-
Set up our Rx project
-
Types of Observables
-
Streams
-
Operators
-
Threading
-
Exceptions
-
Handling real time updates (DEMO)
-
Warnings!
What are we building?
an app with real time updates!
Repository
git clone git@github.com:goofyahead/RxWorkshop.git
Hello Rx!
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.example.tsl057.rxjavaplaygroundjava"
minSdkVersion 24
...
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
implementation 'com.google.firebase:firebase-firestore:12.0.1'
...
implementation "io.reactivex.rxjava2:rxandroid:$RX_ANDROID_VERSION"
implementation "io.reactivex.rxjava2:rxjava:$RX_JAVA_VERSION"
}
Well done!

What is reactive programming?
int result = 0;
int a = 3;
int b = 2;
result = a + b; //result = 5
a++;"In computing, reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change. "
Observable and observers

Observable
Observer
Creating an observable
Observable<Integer> myObservable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onComplete();
}
});Observable<Integer> myObservable = Observable.just(2, 3, 4);
Subscribing/observing
Observer<Integer> observer = new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
// we are subscribed, we get a reference to dispose our subscription
}
@Override
public void onNext(Integer integer) {
// a new value was emitted
}
@Override
public void onError(Throwable e) {
// something bad happened
}
@Override
public void onComplete() {
// stream is finished
}
};
observable.subscribe(observer); <= Only now things start to happen.Types: Observable
Observer<Integer> observer = new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Integer integer) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
};Types: Single
SingleObserver<Integer> obs = new SingleObserver<Integer>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onSuccess(Integer integer) {
}
@Override
public void onError(Throwable e) {
}
};Types: Completable
CompletableObserver obs = new CompletableObserver() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onComplete() {
}
@Override
public void onError(Throwable e) {
}
};Types: Maybe
MaybeObserver<Integer> maybeObserver = new MaybeObserver<Integer>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onSuccess(Integer integer) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
};Types: Flowable
Flowable<String> flowable = new Flowable<String>() {
@Override
protected void subscribeActual(Subscriber<? super String> s) {
s.onNext("hello");
}
};
flowable
.buffer(12)
.onBackpressureDrop()
.subscribe();So many types!

Streams

From a live source
return Observable.create(emitter -> collection.addSnapshotListener((queryDocumentSnapshots, e) -> {
for (DocumentChange document : queryDocumentSnapshots.getDocumentChanges()) {
if (!emitter.isDisposed()) {
Log.d(TAG, document.getDocument().getData().toString());
emitter.onNext(document.getDocument().toObject(Product.class));
}
}
}));//Java stream
Optional<Product> found = productList
.stream()
.filter(product -> (product.id.equalsIgnoreCase(modifiedProduct.id)))
.findAny();Or a list
//Rx Stream
Maybe<Product> productMaybe = Observable.fromIterable(productList)
.filter(product -> (product.id.equalsIgnoreCase(modifiedProduct.id)))
.firstElement();Operators
Observable
.fromIterable(productList)
.flatMap(product -> Observable.just(1,3,4,6))
.firstElement();flatMap
Observable
.fromIterable(productList)
.map(product -> product.name)
.firstElement();map
Operators
Observable
.fromIterable(productList)
.flatMap(product -> Observable.just(1,3,4,6))
.filter(number -> number > 3)
.firstElement();filter
Observable.fromIterable(productList)
.map(product -> product.name)
.distinct();distinct
Single<List<String>> unique = Observable.fromIterable(productList)
.map(product -> product.name)
.debounce(300, TimeUnit.MILLISECONDS)
.toList();debounce
Operators
Observable.fromIterable(productList)
.map(product -> product.name)
.groupBy(s -> {
switch (s.charAt(0)){
case 'a':
case 'c':
return "starters";
case 'r':
case 'w':
return "weird";
default:
return "unknown";
}
});groupBy
Observable.fromCallable(slowCallToBackend())
.map(product -> product.name)
.timeout(3000, TimeUnit.MILLISECONDS);timeout
Operators
Observable.fromIterable(productList)
.map(product -> product.name)
.delay(3, TimeUnit.SECONDS)
.replay(10);replay

and many more
http://reactivex.io/documentation/operators
Threading
usersObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { println(it) }
val usersObservable = Observable.fromCallable {
usersDao.loadAll()
}Multiple sources
Schedulers.computation().
Contains several threads for computations.
The number of threads depends on the number of the processes available for Java on this device.Schedulers.io().
Perfect for the I/O activities, like reading or recording into a database, server requests.Schedulers.newThread().
This thread will create a new thread whenever someone subscribes.
After the work is done, the thread will be finished.Schedulers.trampoline().
Enables you to delay the task in the current thread and queue it.
This scheduler will process its queue and start tasks one by one. Combining calls
Single<Integer> singleA = Single.fromCallable( slowFunctionA() )
.timeout(300, TimeUnit.MILLISECONDS)
.retry(5);
Single<Integer> singleB = Single.fromCallable( slowFunctionB() )
.timeout(600, TimeUnit.MILLISECONDS)
.retry(5);
Single.zip(singleA, singleB, (a, b) -> a + b )
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
simplified threading

Exceptions & errors

Exceptions & errors
repository.getProductObservable()
.filter(product -> product.price < 5)
.subscribe(
product -> listener.onProductOffer(product),
throwable -> Observable.just(new Product())
);repository.getProductObservable()
.filter(product -> product.price < 5)
.subscribe(new Observer<Product>() {
@Override
public void onSubscribe(Disposable d) {}
@Override
public void onNext(Product product) {}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {}
});Show me the code!

From db to observable
public Observable<Product> getProductObservable() {
return Observable.create(emitter -> collection.addSnapshotListener(
(queryDocumentSnapshots, e) -> {
for (DocumentChange document : queryDocumentSnapshots.getDocumentChanges()) {
if (!emitter.isDisposed()) {
Log.d(TAG, document.getDocument().getData().toString());
emitter.onNext(document.getDocument().toObject(Product.class));
}
}
}
));
}
Presenter to view
public void subscribeAllProducts(ProductListener.ModifiedProduct listener) {
repository
.getProductObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(product -> listener.onProductModified(product));
}
public void subscribeOffers(ProductListener.OfferProduct listener) {
repository.getProductObservable()
.filter(product -> product.price < 5)
.subscribe(product -> listener.onProductOffer(product),
throwable -> Observable.just(new Product()));
}Adapter
public void productChanged(Product modifiedProduct) {
Optional<Product> found = productList
.stream()
.filter(product -> (product.id.equalsIgnoreCase(modifiedProduct.id)))
.findAny();
if (!found.isPresent()) {
productList.add(modifiedProduct);
notifyItemInserted(productList.size());
} else {
OptionalInt position = IntStream.range(0, productList.size())
.filter(i -> (productList.get(i).id.equalsIgnoreCase(modifiedProduct.id)))
.findFirst();
found.get().updateWith(modifiedProduct);
notifyItemChanged(position.getAsInt());
}
}Lets do it live!

Stream everything!

but be careful
.subscribe(new Observer<Product>() {
@Override
public void onSubscribe(Disposable d) {
compositeDisposable.add(d);
}
}
Improvements
- LiveData
- LyfeCycleOwners
- Room
- DataBinding
- Full MVVM
References
RxJava 2 changes:
https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0
RxJava article
https://android.jlelse.eu/reactive-programming-for-android-d55bdbb438b4
DataBinding & lifecycle
https://android.jlelse.eu/android-architecture-components-livedata-with-data-binding-7bf85871bbd8
MVVM & Rx
https://proandroiddev.com/mvvm-architecture-using-livedata-rxjava-and-new-dagger-android-injection-639837b1eb6c
Official doc
http://reactivex.io/
Java Streams
http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.htmlQuestions?
contact
arodriguez@tribalscale.com
https://medium.com/@goofyahead

Reactive programming
By Alejandro Vidal Rodriguez
Reactive programming
- 30