43

I've just tried to run my app compiled using Java 8 on an Android 4.0 device. While I'm used to taking great care to look at the Android API levels in the Android documentation to make sure I'm only using APIs that are available on Android 4.0, I'm not so used to making sure I'm not using any features in Java itself that aren't available on Android 4.0.

Consider the following code, it tries to import the initializeScrollbars() API from View class because, for whatever reason, it has been removed from the official SDK:

try {
    final Method initializeScrollbars = android.view.View.class.getDeclaredMethod("initializeScrollbars", TypedArray.class);
    initializeScrollbars.invoke(this, a);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {        
    e.printStackTrace();
}

While this code works fine on my Android 8.0 test system, it doesn't work on Android 4.0. The error is:

Could not find method java.lang.ReflectiveOperationException.printStackTrace

After some research I found out that ReflectiveOperationException is not available before Java 7 and so, apparently, Android 4.0 does not support Java 7.

This makes me wonder: Is there an overview which clearly shows which Android versions come with which Java version? e.g. how can I find out the first Android version that supports Java 7? And how can I find out the first Android version that supports Java 8?

This really must be easy to find but I'm just failing to see it. Googling always leads to results in which people are asking about the Java versions supported by Android Studio, not by Android itself.

So, can anybody please shed some light onto this? I know it must be somewhere really obvious, but I don't seem to find it...

Andreas
  • 9,245
  • 9
  • 49
  • 97
  • The `ReflectiveOperationException` problem goes away if you convert the multi-catch clause to three uni-catch clauses. The inferred type of `e` is the common ancestor of the three exception types, which is `ReflectiveOperationException` in Java 7, and thus the compiler generates a call to `ReflectiveOperationException.printStackTrace()` for the line `e.printStackTrace()`. See [§14.20 in JLS 7](https://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20). – jcsahnwaldt Reinstate Monica Mar 04 '23 at 03:49
  • ...or you could add a completely unrelated exception type to the multi-catch to make the compiler infer a common ancestor that exists in pre-7 Java. For example, if you replace `catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e)` by `catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | ArithmeticException e)` the inferred type of `e` will be `Exception`, and the `ReflectiveOperationException` problem disappears... :-) – jcsahnwaldt Reinstate Monica Mar 04 '23 at 04:45

3 Answers3

31

and so, apparently, Android 4.0 does not support Java 7.

By your definition, Android does not support any version of Java. The java and javax classes in the Android SDK do not exactly match any version of Java, whether a numerical version (e.g., 6, 7, 8) or whatever you want to consider Java SE/EE/ME to be.

Is there an overview which clearly shows which Android versions come with which Java version

In terms of language features (e.g., lambda expressions), quoting the documentation:

Android Studio 3.0 and later supports all Java 7 language features and a subset of Java 8 language features that vary by platform version

Here, "Android Studio" is really referring to the build tools that compile your source code and create the Dalvik bytecode. Current tools can support all Java 7 and a few Java 8 features on all versions of Android. Additional Java 8 features are only available on API Level 24+, usually because they rely upon certain classes that were only added to the Android SDK at that point.

But your concern seems to be with classes and methods, in which case there is no simple mapping of any Java version to any Android version.

Moreover, you are using reflection to hack into framework classes, which means your results will not only vary by Android version but by device model, as device manufacturers can and do change the implementation of framework classes.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 2
    So how can I make sure the Java code works on older Android versions too? For pure Android APIs it's easy, just check the API level, but what about inbuilt Java stuff like `ReflectiveOperationException` which is there on Android 8, but not on Android 4.0. Granted, my example does something which shouldn't normally be done, but couldn't there be other cases in which you use some Java method/class which is there on Android 8 but not on Android 4 and you don't know it? That's a little disturbing. – Andreas Jan 10 '19 at 13:59
  • 2
    @Andreas: "So how can I make sure the Java code works on older Android versions too?" -- set your `minSdkVersion` appropriately, and let the build tools guide you, as you will get complaints from Lint when you use stuff directly that is newer than your `minSdkVersion`. Plus, have thorough test suites. – CommonsWare Jan 10 '19 at 14:03
  • Well, but `minSdkVersion` is set to 14 in my `build.gradle` and I get no warnings about `ReflectiveOperationException` from Lint whatsoever... – Andreas Jan 10 '19 at 14:06
  • 1
    @Andreas: Sure. You're not referencing `ReflectiveOperationException` directly, so there is nothing for Lint to complain about. It would appear that there is some bug in your particular device that you're tripping over, because somebody didn't expect you to be doing what you're doing. This will happen when you try to use reflection to access things outside of the Android SDK. – CommonsWare Jan 10 '19 at 14:50
  • Ok, since the rest of the code seems to work just fine on Android 4.0, the `ReflectiveOperationException` thing is probably just a rare case of bad luck which is not too common. – Andreas Jan 10 '19 at 15:20
  • I'm pretty sure this is outdated now as Android's Dalvik is discontinued – Elijah Mock Dec 31 '19 at 16:11
  • 3
    @EliTheHuman: The Dalvik runtime environment was replaced by ART. This answer refers to Dalvik in the context of the bytecode format, which is still used. – CommonsWare Dec 31 '19 at 17:13
15

From the "Supporting Older Versions" AOSP documentation found at https://source.android.com/setup/build/older-versions#jdk:

Supported versions

Android 7.0 (Nougat) – Android 8.0 (Oreo):

Ubuntu: OpenJDK 8

Mac OS X: JDK 8u45 or higher

Android 5.x (Lollipop) – Android 6.0 (Marshmallow):

Ubuntu: OpenJDK 7

Mac OS X: jdk-7u71-macosx-x64.dmg

Android 2.3.x (Gingerbread) – Android 4.4.x (KitKat):

Ubuntu: Java JDK 6

Mac OS X: Java JDK 6

Android 1.5 (Cupcake) – Android 2.2.x (Froyo):

Ubuntu: Java JDK 5

The documentation does not presently (Feb 2022) make any correlation between Android >=9.0 and JDK (that I could find); I suspect this has been overlooked since it does not appear that the setup documentation has been thoroughly updated since sometime prior to the Ubuntu 20.04 LTS release. It does mention that the latest master branch (Android 12.0.0_r32) comes with a prebuilts folder that has the necessary JDK in it.

claypooj
  • 333
  • 2
  • 6
  • This is talking about which JDK you need if you want to build Android itself from source, which seems unrelated to what OP is asking. – Adam Burley Feb 07 '23 at 16:20
  • Just a remark here, Android 7.0 does not support all features of JDK 8, but Android 8.0 does – Juan C. Aug 29 '23 at 16:58
2

This is just a guess but I think we can tell which Java version an Android OS version has based on the branches of the /platform/prebuilts/jdk/jdk* Git repositories which can be seen at https://android.googlesource.com/:

  • /platform/prebuilts/jdk/jdk8 git repository has branches for: oreo (8), pie (9), 10, 11, 12, 13
  • /platform/prebuilts/jdk/jdk9 git repository has branches for: pie (9), 10, 11, 12, 13__
  • /platform/prebuilts/jdk/jdk11 git repository has branches for: 11, 12, 13
  • /platform/prebuilts/jdk/jdk17 git repository has branches for: 13

With the information above, I think we can assume that...

  • Android 13 has access to Java 8, 9, 11, and 17
  • Android 11 and 12 has access to Java 8, 9, and 11
  • Android Pie (9) and 10 has access to Java 8 and 9
  • Android Oreo (8) has access to Java 8

I didn't see any branches regarding any version older than Oreo so I don't know what and where to look, but I think @claypooj's answer covered that:

  • Android Nougat (7) has access to Java 8
  • Android Lollipop (5) and Marshmallow (6) has access to Java 7
  • Android Gingerbread (2.3), Honeycomb (3), Ice Cream Sandwich (4), Jelly Bean (4.1), and KitKat (4.4) has access to Java 6
  • Android Cupcake (1.5), Donut (1.6), Eclair (2), and Froyo (2.2) has access to Java 5
bmdelacruz
  • 2,366
  • 1
  • 19
  • 26