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.html

Questions?

contact

arodriguez@tribalscale.com

https://medium.com/@goofyahead

Reactive programming

By Alejandro Vidal Rodriguez

Reactive programming

  • 30