Note that there are no global changes in the new version, but support for Java 8 has appeared, and the library has become more convenient to use.
Disclaimer: This article is based on a GitHub review , in addition, we share our mobile developers' impressions of RxJava 3, but we do not pretend to be an exhaustive study - because the new version was released recently and there is not much practical experience yet. If you have any additions, write in the comments, we will be happy to discuss)
Key changes in RxJava 3
So what we see in RxJava 3 :
- Java base version is now increased to 8 *;
- There is support for such language features as:
- Streams
- Stream Collectors
- Optional
- CompletableFeature
* To use the 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 lot of overloads, can always be replaced with time + unit;
- java.util.function - Cannot throw exceptions, and overloads can create unnecessary "ambiguity".
The package structure has also changed:
The changes presented can be divided into 2 groups:
- Behavioral changes
- API changes
Behavioral changes
- All errors will be processed
Previously, one of the problems with RxJava 2 was that in some cases, errors could get lost and not be handled. Now, in RxJava 3, unsubscribing from a source that might throw an error triggers a throwing of this error into the general error handler via RxJavaPlugins.onError ()
- Connectable.reset ()
There was a hot springs issue in RxJava 2: when receiving a ConnectableObservable terminal event, new connections ignored all items and only received the terminate event.
RxJava 3 introduces a function to reset the ConnectableObservable state using the reset () function to allow newly connected subscribers to process data.
- Ability to pause Flowable.publish
In RxJava 3, after a certain number of values are received, the Flowable.publish is paused and the remaining elements are available to subsequent 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 triggered.
- CompositeException.getCause ()
Earlier in RxJava 2, the getCause () method sometimes was a significant memory load, calling the initCause method on each exception, was unstable, and did not always throw an exception chain.
In the new version, this method has been changed from the inside and now generates a chain of errors in the form of a stack trace - by simple formatting of strings.
- Changed parameter validation exceptions
If the parameter is invalid, some operators will now throw IllegalArgumentException instead of IndexOutOfBoundsException:
- skip
- skipLast
- takeLast
- takeLastTimed
- Pre-closing sources for fromX ()
The fromAction () and fromRunnable () libraries have become consistent with the rest of the fromX () calls. The fromRunnable () and fromAction () callbacks will now be closed instantly when called accordingly. In RxJava 2, these operators were closed after the end of the execution of the body of the lambda passed to the parameters.
- Order of resource cleanup when using using ()
The using () statement has a parameter that is responsible for when the used resource will be cleaned up (true - before completion, false - after). In the early version of the library, this parameter was ignored, and the resource was always cleaned up before getting the terminal state, but in RxJava 3 everything works correctly.
API changes
- Exception handling of functional interfaces of the new version of the library has been extended from Exception to Throwable
- New types: Supplier
In RxJava 3, in connection with the extension of exceptions of functional interfaces from Exception to Throwable, a new Supplier type was introduced - analogous to Callable, but with throws Throwable
- In RxJava 3, the operators for converting one data source to another have been changed to specific converters
- Moved components
Due to the fact that RxJava3 will support the Java8 API, a new solution has come to replace individual factory classes: the methods of these factories have become static methods of the Disposable interface.
As it was before:
Now:
- In order to reduce warnings, the DisposableContainer class that was used inside the CompositeDisposable is 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:
Flowable promotions
dematerialize(Function)
Observable promotions
dematerialize(Function)
Maybe promotions
doOnTerminate(Action)
materialize()
Single promotions
dematerialize(Function)
materialize()
Complectable promotions
delaySubscription(long, TimeUnit)
delaySubscription(long, TimeUnit, Scheduler)
materialize()
- concatMap() Scheduler’
- blockingSubscribe() Maybe, Single Completable
- onErrorComplete()
- onErrorResumeWith() Completable
- retryUntil() Single Completable
- switchOnNext() switchOnNextDelayError() Maybe, Single Completable
- dematerialize() Maybe
- fromX()-
- timeInterval() Maybe Single
- toFuture() Maybe Completable
- ofType() Maybe Single
- doOnLifecycle() Maybe, Single Completable
- concatMapX() (X – ) Maybe Single
- concatDelayError() Maybe, Single Completable
- mergeArray() Single
Flowable
startWith(MaybeSource)
startWith(SingleSource)
startWith(ComplectableSource)
Observable
startWith(MaybeSource)
startWith(SingleSource)
startWith(ComplectableSource)
Maybe
startWith(Publisher)
startWith(ObservableSource)
startWith(MaybeSource)
startWith(SingleSource)
startWith(ComplectableSource)
Single
startWith(Publisher)
startWith(ObservableSource)
startWith(MaybeSource)
startWith(SingleSource)
startWith(ComplectableSource)
Complectable
startWith(MaybeSource)
startWith(SingleSource)
- onErrorReturn() Completable
- safeSubscribe() Maybe, Single Completable
- flatMap() Single
- concatEager() concatEagerDelayError() Flowable, Observable, Maybe Single
- fromSupplier()
Java8
Added new operator fromOptional () for Flowable, Observable and Maybe
Added new operator fromStream () for Flowable and Observable
Added new operator fromCompletionStage () for all five data source types
Added new operator mapOptional () for Flowable, Observable, Maybe and Single. It only allows non-empty values to pass.
Added new blockingStream () operator for Flowable and Observable. The operator represents the data stream as a stream, while it is recommended to close the stream upon completion of work with it in order to prevent all kinds of leaks
Added new operators flatMapStream () andconcatMapStream () for Flowable and Observable - allow each item to be converted into a separate stream
Added new operators flattenStreamAsFlowable () and flattenStreamAsObservable () for Maybe and Single - equivalent flatMapStream () operators for Observable and Flowable
What's 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)
- Maybe.flatMapSingleElement() – Maybe.flatMapSingle()
- Callable – Supplier ( )
API
Maybe.toSingle (), replacement - Maybe.defaultIfEmpty ()
subscribe (..., ..., ...,), replacement - doOnSubscribe ()
Single.toCompletable (), replacement - Single.ignoreElement ()
Completable.blockingGet (), replacement - Completable .blockingAwait ()
replay (Scheduler), replace - observeOn (Scheduler)
dematerialize (), replace - deserialize (Function)
onExceptionResumeNext (), replace - onErrorResumeNext (Function)
combineLatest () (with vararg parameter), replace - combineLatestArray ()
fromFuture () (with Scheduler parameter), replacement - subscribeOn ()
concatMapIterable () (with buffer parameter), replacement - concatMapIterable (Function)
Also removed the following methods from TestSubscriber and TestObserver:
How to migrate
Many developers note that migrating from RxJava 2 to RxJava 3 is a rather laborious process. There are two options for making the transition:
- have both versions of the library on your project;
- perform a full migration to RxJava 3, while you can use a special library .
So how to migrate:
- To update the work with the API - to use analogs of those RxJava 2 methods that were subject to change.
- It is important to keep in mind that some third-party libraries are still "sitting" on RxJava 2. To simplify the transition, you can take RxJavaBridge , which hides most of the migration under the hood.
- Retrofit RxJava3CallAdapterFactory .
We've reviewed what's new in RxJava 3. And now let's try to answer the main question - is it worth migrating at all?
RxJava 3 is practically an API improvement. 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 associated with RxJava 2, to work with Java8, you will additionally need to raise minSDK to version 24. Some teams also offer alternative solutions, such as using Kotlin Coroutines.
However, it's worth noting that RxJava 2 is now going into “maintenance” mode, which means that there will only be bug fixes from the updates. Support for the second version is expected to end on February 28, 2021.
! , .