Migration to RxJava 2.x

Migration to RxJava 2.x

RxJava library has already become a standard in Android development. It gives engineers a way to think functionally and reactively. Recently RxJava 2.x was released. You can see the differences comparing to version 1.x here. It was also mentioned that further development of version 1.x will be stopped on the 31th of March 2018. Therefore, we have started slowly migrating to version RxJava 2.x.

We implement all new features using RxJava 2.x, and from time to time we do some refactoring in order to use the most recent version of it.

Here I would like to share some insights from our migration experience, some issues we found, and the way we solved them. This list might be helpful for you, if you decide to switch versions of RxJava.

New types

We started using new types like Completable, Single, Maybe. They were in the experimental phase for a while, but in RxJava 2.x they were released as standard. So in which cases do we use them?

Completable is good when there is some task, which doesn’t return anything. Before we had to use Observable<Void>, which submits null value. Since in RxJava 2.x this is prohibited, Completable type is a perfect data structure for that.

Single is basically used in all network interfaces defined for Retrofit. Because we know that the network call always returns one response, there’s no need to expect to receive more items from the stream.

Maybe can be used if you plan to return Optional value. The following type Observable<Optional<Location>> can be replaced with Maybe<Location>. In our case we had a situation when we needed to retrieve user location. But in some cases location is unavailable and Maybe is ideal solution for such scenario.

Chain new types

Chaining operators is a nice way to keep your app functional. For Maybe and Single there is a method toObservable(). The interesting part with Completable is it also has toObservable(). But after that Observable doesn’t submit any value and doesn’t complete since Completable doesn’t have any value and has already completed. However, there is a method andThen() which accepts a source of the stream for continuation. Additionally there are the methods like flatMapObservable(), flatMapCompletable(), flatMapSingle(), which help to chain different types together.

RxJava Retrofit Adapter

If you use RxJava with Retrofit, you should use the appropriate adapter for Retrofit in this case. There are official adapters from Square for RxJava 1.x and for RxJava 2.x. It’s possible to have two of them at the same time - for version 1.x and 2.x.

Interoperability

Once your project uses both of the versions of RxJava, you arrive at a question: How can I switch or compound both versions at the same time? And there is an answer: There is a library RxJavaInterop. It helps a lot in converting types of different versions of RxJava. Now instead of global refactoring of the app, you can do it in small pieces applying the library. Check it out and you will see how useful it is.

Testing

Writing unit tests for classes and methods using RxJava also changed a bit. First, there are no Schedulers.immediate() and ImmediateScheduler anymore. Instead TrampolineScheduler and Schedulers.trampoline() should be used.

Second, for each method returning RxJava stream type (Observable, Completable etc.) there is a method test(). It subscribes to the observable and returns TestObserver on which you can do assertions. As mentioned, the type now is TestObserver instead of TestSubscriber. But, you can find TestSubscriber as well. Well, TestSubscriber is used now for Flowable and TestObserver for Observable.

Default subscribe() method

Something interesting is also happening with subscribing to the stream. All the methods subscribe() are returning now Disposable type, except one. For Observable it has the signature void subscribe(Observer<? super T> observer). Pay attention next time if you want to pass a subscriber (observer) to subscribe() method. In RxJava 1.x it was returning Subscription. But in RxJava 2.x it’s void.

And the reason of that is:

Because Reactive-Streams base interface, org.reactivestreams.Publisher defines the subscribe() method as void, Flowable.subscribe(Subscriber) no longer returns any Subscription (or Disposable). The other base reactive types also follow this signature with their respective subscriber types.

Error handling

The last point which I wanted to mention is the updated error handling method. It is very important. Our app started crashing in some cases when we started using RxJava 2.x. And the reason was that the stream already completed and the error occurred later. It can happen for different reasons. You can find good explanation of it in official manual in section Error Handling. Don’t ignore it and make sure all errors are handled properly.

Summary

In this article I wanted to share what we faced with migration to RxJava 2.x. Before migrating yourself, I would recommend that you to go through the list of changes in RxJava 2.x. It has enough information to alert you to any potential problems.