Note that in the new version there are no global changes, but Java 8 support has appeared, and the library has become more convenient to use.
Disclaimer: This article is based on github reviewIn addition, we share the impressions of our mobile developers from RxJava 3, but do not claim to be exhaustive research – because the new version came out recently and there is not much practical experience. If you have additions, write in the comments, we will be happy to discuss)
Key Changes in RxJava 3
So what we see in RxJava 3:
- The basic version of Java is now increased to 8 *;
- Support has been added for such language features as:
– Stream Collectors
*For use Java8 API you need to raise minSDK to version 24.
In turn, the developers removed support for such features as:
- java.time.Duration – generates a large number of overloads, can always be replaced with time + unit;
- java.util.function – cannot throw exceptions, while overloads can create unnecessary “ambiguity”.
The package structure has also changed:
The presented changes can be divided into 2 groups:
- Behavioral changes
- API changes
- All errors will be processed.
Earlier, one of the problems of RxJava 2 was that in some cases errors could be lost and not be processed. Now in RxJava 3, unsubscribing from a source that may throw an error initiates the forwarding of this error to the common error handler via RxJavaPlugins.onError ()
- Connectable.reset ()
There was a problem with hot sources in RxJava 2: when receiving a ConnectableObservable terminal event, new connections ignored all elements and received only the terminate event.
RxJava 3 introduced the ConnectableObservable reset function with the reset () function to enable newly connected subscribers to process data.
- Ability to pause Flowable.publish
In RxJava 3, after receiving a certain number of values, Flowable.publish is paused, while the remaining elements are available to the following subscribers.
- Processor.offer () null parameter
If you try to call PublishProcessor.offer (), BehaviourProcessor.offer () or MulticastProcessor.offer () and pass null, a NullPointerException is thrown instead of passing an error to the onError handler, and a terminal state is raised.
- CompositeException.getCause ()
Earlier in RxJava 2, the getCause () method sometimes significantly loaded the memory, calling the initCause method for each exception, it was unstable, it did not always generate an exception chain.
In the new version, this method is changed from the inside and now generates a chain of errors in the form of a stack trace – by simple formatting of strings.
- Parameter validation exceptions changed
With an invalid parameter, some operators will now throw an IllegalArgumentException instead of an IndexOutOfBoundsException:
- Pre-Closing Sources for fromX ()
The fromAction () and fromRunnable () libraries became consistent with the rest of the fromX () calls. Callbacks fromRunnable () and fromAction () will now be closed instantly with the corresponding call. In RxJava 2, these statements were closed after the execution of the lambda body passed to the parameters.
- The procedure for clearing a resource when using using ()
The using () operator has a parameter responsible for when the used resource will be cleared (true – before completion, false – after). In the earlier version of the library, this parameter was ignored, and the resource was always cleared until the terminal state was obtained, but in RxJava 3 everything works correctly.
- Exception handling of functional interfaces of the new version of the library is expanded from Exception to Throwable
- New Types: Supplier
In RxJava 3, due to the extension of functional interface exceptions from Exception to Throwable, a new type of Supplier is introduced – an analogue of Callable, but with throws Throwable
- In RxJava 3, the conversion operators from one data source to another have been changed to specific converters
- Relocated Components
Due to the fact that RxJava3 will support the Java8 API, individual factory classes have been replaced by a new solution: the methods of these factories have become static methods of the Disposable interface itself.
As it was before:
- In order to reduce warnings, the DisposableContainer class that was used inside CompositeDisposable has been made part of the public API
- Some of the operators in RxJava 2 were at the experimental stage, and in the third version they became standard operators:
- In the new version of the library, the operator concatMap () for flexibility of flow control was overloaded Scheduler’om
- Added operator overload blockingForEach () with the ability to specify a buffer size
- Operator blockingSubscribe () now available for use in Maybe, Single and Completable
- Operator onErrorComplete () now available in all five types of data sources
- Operator onErrorResumeWith () now available for use in Completable
- Operator retryUntil () now available for use in Single and Completable
- Operator switchOnNext () and switchOnNextDelayError () now available for use in Maybe, Single and Completable
- Operator dematerialize () now available for use in Maybe
- There was an update of various types of data sources fromX () – operators
- Operator timeInterval () now available for use in Maybe and Single
- Operator toFuture () now available for use in Maybe and Completable
- Operator ofType () now available for use in Maybe and Single
- Operator doOnLifecycle () now available for use in Maybe, Single and Completable
- The concatMapX () operator (X is another type of data source) is now available for use in Maybe and Single
- Operator concatDelayError () now available for use in Maybe, Single and Completable
- Operator mergeArray () now available for use in Single
- Operator startWith () now available for use in all five types of data sources
- Operator onErrorReturn () now available for use in Completable
- Operator safeSubscribe () now available for use in Maybe, Single and Completable
- Operator flatMap () now available for use in Single
- Operator concatEager () and concatEagerDelayError () now available for use in Flowable, Observable, Maybe, and Single
- Added operator fromSupplier () to all five types of data sources
New with Java8
New operator added fromOptional () for Flowable, Observable and Maybe
New operator added fromStream () for Flowable and Observable
New operator added fromCompletionStage () for all five types of data sources
New operator added mapOptional () for Flowable, Observable, Maybe, and Single. It only skips non-empty values
New operator added blockingStream () for Flowable and Observable. The operator presents the data stream in the form of a stream, while it is recommended to close the stream upon completion of work with it, in order to prevent all kinds of leaks
What is renamed
- Instead of startWith () – startWithArray (), startWithIterable (), startWithItem ()
- Instead of onErrorResumeNext () – onErrorResumeWith ()
- Instead of zipIterable () – zip ()
- Instead of combineLatest () (with an array argument) – combineLatestArray (), combineLatestArrayDelayError ()
- Instead of Single.equals () – sequenceEqual (SingleSource, SingleSource)
- Instead of Maybe.flatMapSingleElement () – Maybe.flatMapSingle ()
- Instead of Callable – Supplier (when using lambdas only recompilation is required)
What is removed from the API
Maybe.toSingle (), replacement – Maybe.defaultIfEmpty ()
subscribe (…, …, …,), replacement – doOnSubscribe ()
Single.toCompletable (), replacement – Single.ignoreElement ()
Completable.blockingGet (), replacement – Completable.blockingAwait ()
replay (Scheduler), replacement – observeOn (Scheduler)
dematerialize (), replacement – deserialize (Function)
onExceptionResumeNext (), replacement – onErrorResumeNext (Function)
combineLatest () (with vararg parameter), replacement – combineLatestArray ()
fromFuture () (with Scheduler parameter), replacement – subscribeOn ()
concatMapIterable () (with buffer parameter), replacement – concatMapIterable (Function)
The following methods have also been removed from TestSubscriber and TestObserver:
How to migrate
Many developers note that migrating from RxJava 2 to RxJava 3 is a rather time-consuming process. To make the transition, there are two options:
- have both versions of the library on your project;
- complete migration to RxJava 3, while you can use a special the library.
So how to migrate:
- Actualize work with the API – use analogues of those RxJava 2 methods that were subject to change.
- It is important to consider that some third-party libraries are still “sitting” on RxJava 2. To simplify the transition, you can take RxJavaBridgethat hides most of the migration under the hood.
- When used in a Retrofit project, pull up RxJava3CallAdapterFactory or make your own factory to correctly map the response.
We looked at what’s new in RxJava 3. Now let’s try to answer the main question – is it worth migrating at all?
RxJava 3 is practically an improvement to the API. Due to the fact that there were no fundamental changes, in general there is no need to migrate to the latest version right now. The transition itself requires effort, while many third-party libraries are still connected with RxJava 2, to work with Java8, you will additionally need to raise minSDK to version 24. Some teams have alternative solutions – for example, use Kotlin Coroutines.
However, it is worth noting that RxJava 2 now goes into “maintenance” mode, and this means that there will only be bug fixes from the updates. Support for the second version is expected to end on February 28, 2021.
Thanks for your attention! We hope you find this material helpful.