Matt’s Tidbits #72 — Migrating from RxJava 2 -> 3

Matthew Groves
4 min readJun 16, 2020

--

Last week’s was a difficult post to write, which discussed issues of racism, bias, and privilege (and a new feature of Android Studio). This time, I want to share my story of migrating from RxJava 2 -> 3.

Staying current with the libraries you use in your app is essential to maintaining a secure, performant application. One such library is RxJava, which fairly aggressively releases new versions and deprecates old ones. RxJava 1 is now officially no longer supported, and even RxJava 2 will only continue to receive updates (critical security patches, etc.) until February of 2021. If you remember the RxJava 1-> 2 migration, you may remember it was fairly painful — many things were renamed, and dropping support for null values in streams often required a lot of refactoring and cleanup to avoid runtime crashes.

The good news is, the RxJava 2 -> 3 migration is much more straightforward! The RxJava team has a very helpful article highlighting the differences: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-3.0

Here is the approach I took when migrating:

  1. Convert all gradle imports from RxJava 2 to RxJava 3 (implementation “io.reactivex.rxjava3:rxjava:3.0.4”).
  2. Do a find-and-replace to change code imports to the new packages (but don’t “Optimize Imports” yet — I saved this for after my PR had been approved so it would make the review process easier):
I did the “Core” one last — it’s a little tricky to find all of these, but when you try compiling your code, they will turn up — things like Flowable, Observable, Single, Maybe, Completable all fall into this package (along with their various Transformers).

3. Try to compile! You will likely see some errors. Then, start fixing them one-by-one. Here are some of the ones I ran into:

  • The 4-argument subscribe() method for Observable has been removed — if you need to specify what happens on subscribe, use the .doOnSubscribe() method.
  • The startWith() method for Flowable/Observable has been split into multiple variants — either startWithIterable(), startWithItem(), or startWithArray().
  • The flatMapSingle() method for Maybe has been replaced by a different function (originally called flatMapSingleElement()) — to achieve the previous behavior, now just call .toSingle().
  • The Timed class now throws a runtime Exception if you try to pass in null for the initial value.
  • The assertSubscribed(), assertNotTerminated(), and several other TestSubscriber methods have been removed, as they were determined to really only be useful internally to the RxJava library itself.
  • The accept() method for the Consumer class now throws a Throwable instead of an Exception.
  • The onErrorResumeNext() method has been renamed to onErrorResumeWith().
  • The methods in the Disposables class have been merged into the Disposable interface.

As a result of this upgrade, some other related libraries also needed to be updated, including:

  • RxLifecycle (to version 4+) (Gradle import + package rename to com.trello.rxlifecycle4)
  • RxDogTag (to version 2+) (Gradle import + package rename to rxdogtag2)
  • RxRelay (to version 3+) (Gradle import + package rename to com.jakewharton.rxrelay3)
  • RxJavaInterop (to version 3+) (Gradle import + package rename to hu.akarnokd.rxjava3.interop)
  • Retrofit (to version 2.9+) (Gradle import, package rename to retrofit2.adapter.rxjava3, and a new call adapter — RxJava3CallAdapterFactory)
  • RxAndroid (to version 3+) (Gradle import + package rename to io.reactivex.rxjava3.android)

One last thing — if you find yourself needing to interface with older RxJava code (from other libraries — hopefully your app is consistent and uses the same version everywhere), there are two helper libraries you should know about:

  1. RxJavaInterop (converting from RxJava 1 <-> 2/3): https://github.com/akarnokd/RxJavaInterop
  2. RxJavaBridge (converting between RxJava 2 <-> 3): https://github.com/akarnokd/RxJavaBridge

I find it slightly irritating that these are separate libraries, but these are both VERY helpful if you want to keep your app on a single version of RxJava, as not all libraries have been updated to use RxJava3 yet — for instance, RxLifecycle, Retrofit, and RxRelay have all only been updated with RxJava 3 support in the last month.

So, if you have a little time on your hands, RxJava 3 now seems ready for primetime use! And thankfully, my own experience was that this conversion was fairly painless — the entire conversion was completed within 1 work day.

Please share your own experiences/tips for making the leap to RxJava 3, and let me know if you have any questions! And, please follow me on Medium if you’re interested in being notified of future tidbits.

This tidbit was discovered on June 10, 2020.

--

--

Matthew Groves

Digital Products NE Mobile Capability Co-Lead & Senior Software Engineer in Boston, MA — https://www.linkedin.com/in/matthew-groves-85677631/