SolvedRxJava 3.x: Potential migration/compatibility issues

Intro:

Since RxJava has a very compelling interface it is widely used as such. Some libraries also use it internally. RxJava 2 and 3 have some breaking changes but they both live in same packages.

Problem:

(I am not an expert in this field but here it goes)
Dependencies in Java are resolved by the full name (i.e. package + class name). Since the package name has not changed between RxJava 2 and 3 there are duplicate classes found during compilation. If two libraries (first level dependencies) have a dependency different major versions of RxJava (as a second level dependency to your app) they will not compile together.

Using gradle it is possible to make a first level dependency use a different second level dependency than it declares by using the below code in build.gradle file:

resolutionStrategy.dependencySubstitution {
    substitute module('io.reactivex.rxjava2:rxjava') with module('io.reactivex.rxjava3:rxjava:3.0.0-RC1')
}

But then there is a good chance that some of those libraries will face binary incompatibilities introduced between RxJava 2 and 3.

This poses a potential logistics problem for the applications consuming RxJava based libraries. There is no way to ensure that those libs dependent on RxJava 2 and 3 will work together — the app developer would need to substitute libs that do not allow to be used with one a specific RxJava version or postpone migration. Developers would potentially need to wait until all their dependencies will migrate to RxJava 3 before they can switch. There is no way to create an interop between RxJava 2 and 3 like it was the case with RxJava 1 and 2.

Question:

Has this problem been considered?

Ideas:

  1. Add new RxJava 3 operators to RxJava 2.3.x. E.g.:
  • Supplier<T>, Observable/Single.defer(Supplier<T>) but keep Observable/Single.defer(Callable<T>) (mark it as @Deprecated and point to the new function), etc.
  • Observable.startWithItem(T) but keep Observable.startWith(T)
  • others if possible
    This could make a usable subset of RxJava 2 binary compatible with RxJava 3 allowing it to easily swapped at a later point when adoption is high enough.
  1. Keep only Observable, Flowable, Single, Completable and their .subcribe() functions in the main package so they could be exposed as an API of all libraries that are of RxLibrary kind and the operator implementations in some extensions / other packages. This could make the usage more cumbersome in Java (but Kotlin language has extensions so this could be mitigated somewhat). This could make the Rx API more future-proof

This issue is a place for discussion (while somewhat related to #6524)

20 Answers

✔️Accepted Answer

After careful considerations of all target environments (Android/Desktop/Server) as well as any future major versions targeting Java 9+, I decided the path with the least problems will be having RxJava 3 reside in the new group id and in a new package entirely:

Group ID: io.reactivex.rxjava3
Package: io.reactivex.rxjava3.**.

In addition, the base classes and interfaces (i.e., Flowable, FlowableSubscriber, Observable) will be moved to a subpackage core: io.reactivex.rxjava3.core.Flowable. A reason for this is that with modules, opening up io.reactivex.rxjava3 opens up all subpackages, including internal which can't be hidden then on as far as I know.

Since Flowables are Publisher's, interoperation between the v2 and v3 variants is a given: either use them as is with parameters/arguments declared as Publisher or use Flowable.fromPublisher.

The other types won't be such lucky as ObservableSource is present in both v2 and v3 and isn't external unlike Reactive Streams. Those can't talk to each other without an actual (trivial) bridge library.

Other Answers:

I have an android app that depends on 12 libraries all depending on rxjava2. I don't understand my path forward.

Do I need to wait for all 12 to upgrade if one does and uses a new api?

The breaking changes are not related to the groupId change and we need to stop conflating the two problems.

The binary incompatibility is a problem no matter what groupId is chosen. You've offered:

I'm sure I can help with making those libraries version agnostic.

And the underlying cause of Zac's issue is just that.

However, what Zac was drawing attention to is the hoop-jumping now required in the build system to ensure that two copies of RxJava do not end up on the classpath. This is entirely unrelated to the binary incompatibility and would be required even if 3.x was 100% compatible with 2.x.

The package name and the groupId are fundamentally linked. If one changes, the other should. If one does not change, the other should not.

Either RxJava 3 should reside in io.reactive.rxjava2 groupId or it should change it package name.

More Issues: