in   Android App Development   Software Development   App Development   Mobile App Development   Android   Software Engineering

Combining RXJava2 Completable with Observable in Android app development

When working with RxJava2 as a core of your project, it’s very likely you will encounter a use case for Completable. There’s a high chance that you will also combine it with, for example, Observable. Then, probably you would like to use flatMap as common in Rx world and there comes flatMapCompletable but you need to be aware where you use them.

zulmaury-saavedra-zh0J32MrJfA-unsplash

Shortly about Completable

Completable is a type of Observable that doesn’t emit any value but emit only two events - onError and onCompleted. You should use it when you don’t care about the value returned from the operation, for example, saving some data in the database. You care only if the operation completed successfully or it returned an error.

Combining Completable with Observable

Imagine a real use case in the Android environment. You have a PublishRelay which triggers when a user clicks a button. Nextly, in the chain which is starting from this relay, you want to save a value to Shared Preferences. This is the ideal scenario to use Completable as you don’t need to return any value. After completion, we would like to call other function().

So it could look like this:

 

buttonIntentRelay

   .flatMapCompletable {

       Completable.fromAction { saveToSharedPreferences(it) }

           .andThen(Observable.just(Unit))

   }

   .subscribe { function() }

What can you find out is that function() will never be called. To find out what’s going on here, let’s write unit test for that logic. In general, unit tests are extremely useful for Rx chains debugging. So let’s try to imitate this chain:

 

@Test

fun `verify if subscribe will be executed`() {

   val observable = Observable.just<Unit>()

   val completable = Completable.fromAction { println("Completable") }

 

   observable.flatMapCompletable { completable }.subscribe { println("Subscribe") }

}

 

The output will be:

Completable

Subscribe

In the test environment subscription is being called as supposed.

So, what’s wrong here?

Let’s check what docs say about the flatMapCompletable:

“Maps each element of the upstream Observable into CompletableSources, subscribes to them and waits until the upstream and all CompletableSources complete”.

So this simply means that the source, in our case it’s buttonIntentSubject, needs to complete to pass through the flatMapCompletable combined with. This means that you should never combine them with Relays, Subjects which you won’t call onComplete on or, in most cases, anything that won’t complete immediately. It will work fine with e.g. Single.just().

The solution

Completable can fit into Observable chain by combining flatMap with andThen(). After completing action it will subscribe to the given source and return its value.

Important note: remember to use correct brackets while working with andThen().

Assuming this, let’s modify first code snippet:

 

buttonIntentRelay

   .flatMap {

       Completable.fromAction { saveToSharedPreferences(it) }

           .andThen(Observable.just(Unit))

   }

   .subscribe { function() }

 

Now the function() will be called right after successful save to preferences.

Tests explanation

After all of that there’s one more thing left — why did test succeed? The answer is very simple but can cause wasting an hour. In the first test I used Observable.just and in the production code, it was PublishSubject. The main difference was that Observable.just completes right after emitting value which in the end makes flatMapCompletable pass.

 

Real imitation should look like this:

@Test

fun `verify if subscribe will be executed`() {

   val observable = PublishRelay.create<Unit>()

   val completable = Completable.fromAction { println("Completable") }

 

   observable.flatMapCompletable { completable }.subscribe { println("Subscribe") }

   observable.accept(Unit)

}

Now the output will be “Completable” only. Replacing flatMapCompletable will solve the issue:

observable.flatMap { completable.andThen(Observable.just(Unit)) }.subscribe { println("Subscribe") }

To sum up

  1. 1. Be aware when using flatMapCompletable, use it only if you are sure that everything up in the chain completes.
  2. 2. If you need to combine Completable with source that doesn’t complete, then use flatMap with andThen().
  3. 3. In tests imitate your production code as much as you can. The difference may bring the wrong conclusions.

  4. We're hiring Android and Senior Android Developers in Krakow, Poland! 

More articles on Android App Development.

 
Daniel Rodak

By

Android Developer with passion and lots of ambition. Hungry for knowledge in both technical and soft areas. A true warrior who fights on the battlefield until the last moment and is not afraid of challenges. #kotlin-first