Skip to content

3.x release vs. Java 8 support on Android #6695

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
akarnokd opened this issue Oct 30, 2019 · 16 comments
Closed

3.x release vs. Java 8 support on Android #6695

akarnokd opened this issue Oct 30, 2019 · 16 comments
Labels
3.x Android Discussion Java 8 Issues and PRs related to Java 8 support
Milestone

Comments

@akarnokd
Copy link
Member

Recently, the news came that Android Studio 4 will support a so-called "desugaring" of Java 8 APIs (i.e., some kind of auto-backport for target devices not supporting newer API levels as I understand). This would allow RxJava 3 to increase the baseline support to Java 8 on its surface API:

  • Stream support
  • Stream Collectors support
  • Optional support
  • CompletableFuture support
  • Use site non-null annotations

Some features won't be supported:

  • java.time.Duration: allocates, we'll stick to time+unit
  • java.util.function: these can't throw Throwables, overloads would create bloat and/or ambiguity

The main issue with Android's Java 8 support is timing related to the pledged release schedule of RxJava 3.x, end of December 2019. AGP 4 may not release for months, inclining us to:

  • Wait for AGP's release, effectively parking RxJava 3 for months as release candidate. Drawback: AGP may slip a lot.
  • Release on schedule with Java 6 baseline. Drawback: AGP may release shortly after and thus
    • we'd be stuck on 3.x for years;
    • we'd have to start working on 4.x undermining 3.x in a sense, also adding more maintenance burden;
    • we release 3.1 with baseline 8, abandoning 3.0 and possibly violating some pledge;
  • Release on schedule with Java 8 baseline. Drawback: 3.x improvements won't be available to many at release.
  • Release on schedule with Java 8 bytecode output but no 8 API use, add those in 3.1. Drawback: despite best efforts, this could cause problems with existing desugaring/tooling.

The same issue is likely to arise with the next step, Java 9 support on Android. From our perspective, support for the Flow.* interfaces would be just enough as none of the other 9 features would work (VarHandles - field reflection anomalies?, Cleaner - portable?, Modules?). Beyond 9, the next major change to RxJava would have to come due to value types (no version ETA yet). Loom/Fibers is likely simply an API expansion, not a fundamental change.

Suggestions, ideas welcome.

@slisaasquatch
Copy link
Contributor

Just some of my thoughts.

I'm not a fan of the 1st option, because literally nobody wins. People can still use RxJava 2 before AGP 4 comes out. I think the 3rd option makes a lot more sense than the 1st.

For Java 9 support, only supporting Flow.* interfaces to me doesn't justify requiring Java 9, as interoperating between the interfaces is pretty easy.

If I had to pick an option, I'd stick with Java 6. I've been using RxJava 2 with Java 8 on the server side, and not supporting Java 8 out-of-the-box does not feel like much of a hindrance, since the interop library is pretty nice and easy to use. Also, not requiring Java 8 is IMO one of the distinguishing features of RxJava vs Reactor Core.

I'm also okay with supporting Java 8 from the start and releasing on schedule. If RxJava 3 is set to support Java 8, it makes no sense to me to delay the initial release or to release it without Java 8 features.

@aaloise
Copy link

aaloise commented Nov 4, 2019

In my opinion, if Java 6 is a must then go with RxJava2. The best option to me seems to be the third, although it means postponing some improvements that may come up gradually in the form of patches.

And maybe Java 11 would be a better choice than Java 9 in the future? Just thinking out loud...

@slisaasquatch
Copy link
Contributor

And maybe Java 11 would be a better choice than Java 9 in the future? Just thinking out loud...

I think requiring a newer Java version only makes sense if that version has features that are beneficial to RxJava. For Java 8 it's Streams, CompletableFutures, etc. For Java 9 it's Flow.* and potentially Cleaner? I don't think Java 11 has anything worthwhile.

@cardamon
Copy link
Contributor

cardamon commented Nov 7, 2019

+1 for option 3

@JakeWharton
Copy link
Contributor

From Android's perspective, you do not need to wait for desguaring to ship to use the types mentioned.

  • Stream: Presumably this would only exist as a static factory for creating an Observable/Flowable from a Stream in which case this would only be callable on API 24+.
  • Collector: This would exist as a blocking consumer member method on Observable/Flowable which would only be callable on API 24+
  • CompletableFuture: Factory methods and conversion method for Completable and Single which would only be callable on API 24+
  • Optional: Not sure what this would be used for. Factory method for Maybe? If so, would only be callable on API 24+
  • TYPE_USE annotations: Have been removed by the build tooling for at least a year or two.

As long as none of the critical path uses these types and they're only used as leaf sources created by factories or leaf converters implemented as operators the rest of the API will work fine and these types will only be usable on API 24+.

When desugaring does ship, these APIs will magically work on older APIs without any change necessary. There is no need to wait for it to launch.

As to Duration, while you shouldn't use it internally because it allocates, it's still very useful for APIs that accept timeouts or intervals so that you can carry the scalar and unit as a single value and store it as a type-safe constant. Internally, RxJava can immediately convert it to millis or its preferred unit. This, also could still be used today provided it's done as overloads which immediately convert into long. Those APIs would only work on API 26+ until desugaring launches when they'd become magically available on older versions.

So, basically, I vote 3 as well.

@JakeWharton
Copy link
Contributor

As to Duration, while you shouldn't use it internally because it allocates, it's still very useful for APIs that accept timeouts or intervals so that you can carry the scalar and unit as a single value and store it as a type-safe constant. Internally, RxJava can immediately convert it to millis or its preferred unit. This, also could still be used today provided it's done as overloads which immediately convert into long. Those APIs would only work on API 26+ until desugaring launches when they'd become magically available on older versions.

As an example, OkHttp does this:

https://github.com/square/okhttp/blob/7e4870537e33e2f0c4ea39f97b3a7d41a6ba0711/okhttp/src/main/java/okhttp3/OkHttpClient.kt#L829-L846

@ZacSweers
Copy link
Contributor

+1 to everything Jake mentioned. It's quite standard to compile against Java 8 in Android projects these days since the original introduction of Desugar. Android's linter protects against usage of any Java 8 APIs that wouldn't be covered under desugar, so there's no danger for consumers either as long as they're just top-level conversion APIs like Jake described above.

@akarnokd
Copy link
Member Author

akarnokd commented Dec 2, 2019

What do you mean by "leaf"? The following API integrations would be possible with Java 8:

public static <T> Flowable<T> fromStream(Stream<T> stream);

public static <T> Flowable<T> fromCompletionStage(CompletionStage<T> cs);

public static <T> Flowable<T> fromOptional(Optional<T> option);

// -----

public Stream<T> blockingToStream();

public CompletionStage<T> firstElementStage();

public CompletionStage<T> firstOrErrorStage();

public CompletionStage<T> singleElementStage();

public CompletionStage<T> singleOrErrorStage();

public CompletionStage<T> lastElementStage();

public CompletionStage<T> lastOrErrorStage();

public <T, A, C, R> Single<R> collect(Collector<T, A, C, R> collector);

public <R> Flowable<R> mapOptional(Function<T, Optional<R>> mapper);

public <R> Flowable<R> flatMapStream(Function<T, Stream<R>> mapper);

So 3 static methods 10 instance methods with otherwise isolated implementations (existing operators wouldn't use Java 8 features such as Objects or Java 8 types, nor the backing implementations of these).

One risk is Java 8's Objects by the way, as a Java 8 compiler sometimes inserts it into the compiled output even though the source didn't mention it (example: #5966).

@slisaasquatch
Copy link
Contributor

I'm pretty sure Objects is available on Java 7. Also, wasn't #5966 caused by error-prone?

@JakeWharton
Copy link
Contributor

Objects.requireNonNull has also been desugared automatically since AGP 3.0.

@akarnokd
Copy link
Member Author

I have cobbled together a small library that uses Java 8 API and features.

I've created a basic Android Studio 3.5.2 project using it from maven local and kept modifying the compileSdkVersion, minSdkVersion and targetSdkVersion to 14, 24 and 29.

  • 14: Methods with Stream, CompletionStage, Duration and Optional won't compile
  • 24: Methods with Duration won't compile
  • 29: okay

Also tried AS 4 canary 5

  • 14: CompletableFuture gives IDE error (minSdk >= 24 required), the project deploys successfully but fails at runtime with missing class. CompletionStage interface gives no error but its methods do.
  • 24: okay
  • 29: okay

@JakeWharton
Copy link
Contributor

What do you mean by "leaf"?

Having no internal callers. All of your listed examples above seem like they fit the bill. As long as these operators and factories are not used internally (unless by overloads/similar functionality), they'll simply be unused for people who cannot use those types due to the NewApi lint check.

@akarnokd
Copy link
Member Author

If I try the library with a JavaVersion.VERSION_1_7 android project, not calling any of the JDK 8 APIs, I get the error

The dependency contains Java 8 bytecode. Please enable desugaring by adding the following to build [...]

requiring the compile options set to 1.8. Is such version leap on a project otherwise not using Java 8 dependencies or features going to be fine?

@JakeWharton
Copy link
Contributor

It is becoming common for libraries to do this nowadays, yes. Java 8 is ancient at this point, and the fact that the Android tooling has not made it the default is ridiculous. OkHttp did it 9 months ago and it's one of the most popular libraries on the platform: https://developer.squareup.com/blog/okhttp-3-13-requires-android-5/.

@akarnokd akarnokd added the Java 8 Issues and PRs related to Java 8 support label Dec 17, 2019
@akarnokd
Copy link
Member Author

Okay. Let's upgrade to RxJava 8 baseline and release without delaying too much.

@akarnokd
Copy link
Member Author

Progress tracked in #6776.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.x Android Discussion Java 8 Issues and PRs related to Java 8 support
Projects
None yet
Development

No branches or pull requests

6 participants