source/targetCompatibility in Gradle
When working with the Android Gradle plugin you are really likely to run into a block of code that looks like this:
compileOptions {
sourceCompatibility JavaVersion.VERSION_xx
targetCompatibility JavaVersion.VERSION_xx
}
sourceCompatibility
- restricts language features that we can use in our codebase. Usually is set up to a version of the JDK used for compilation. As an example using JavaVersion.VERSION_11
enables us to use var
keyword for local variable type definition.
targetCompatibility
- controls the version of generated class files or the lowest Java version the program can run on. As an example using JavaVersion.VERSION_1_8
generates class files with version 52.
The latest AGP (release 8.0) now requires JDK 17 to run and many folks got confused that this means they should also bump target/sourceCompatibility
to VERSION_17
. While you should always be using the latest JDK (20 at the time of writing) to run your Gralde tasks when building Android projects it’s kinda pointless to bump target/sourceCompatibility
above VERSION_11
, because there’s currently not much support for desugaring newer apis/language features by D8, or above VERSION_8
when using Kotlin extensively since the Kotlin compiler doesn’t produce bytecode which would take advantage of newer features.
To make sure that everybody on your team uses the same JDK for running Gradle and compiling the project Gradle now comes with a handy new set of tools for defining the version of JDK to use called jvmToolchain
. Once fixed in AGP we could replace the compileOptions
configuration block with:
java {
toolchain {
languageVersion = JavaLanguageVersion.of(19)
}
}
that would take care of managing source/targeCompatibility
while also managing the installation of JDK. I found using toolchain
with a combination of SDKMAN for managing JDK to work really well and expect this to become the default.
Of course, this solution isn’t without problems. While trying to set up toolchain
you could run into issues with 3rd party libraries that are not compatible with much higher versions of defined compatibility. As an example, I ran into Dagger Hilt plugin issues while trying to setup toolchain
with languageVersion = JavaLanguageVersion.of(20)
resulting in compile-time errors, and had to revert back to the original configuration to be able to use JDK 20 to run Gradle.
Since the project I’m currently working on uses Kotlin extensively I’m keeping source/targetCompatibility
set to JavaVersion.VERSION_1_8
. With the latest update to Kotlin tries to read jvmTarget
from the enviroment which would not match the compatibility version defined in the android
plugin definition, so to keep the versions in sync one needs to configure them manually using:
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile).configureEach {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}