Use Java8 APIs on Older Android Versions
The less adoption of Java8 APIs in the Android Codebase is because of the lack of support on the older versions. Even though Java8 was released in 2014, it took a while for Google to resolve the issue.
Existing Issue
We will take an example to understand the issue in detail. Let's say we have an array list that contains countries and we need to check if the desired country is available in the list.
The code to achieve it would look something like below.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// Find the country with name India
val country = countryArrayList.stream().filter { it == "India" }.findFirst()
if (country.isPresent) {
// Country is present in the list
} else {
// Country is not present in the list
}
} else {
// For loop to filter and find the item on the older version
var countryPresent = false
countryArrayList.forEach {
if (it == "India") {
// Country is present in the list
// Update the boolean value
countryPresent = true
}
}
if (countryPresent) {
// Country is present in the list
} else {
// Country is not present in the list
}
}
The above code is huge. What we are essentially doing was that we are using Java8 Stream APIs to filter and check whether the desired country exists or not on Android versions above 24 (Android N) and on the older version, we are using simple for loop with a boolean for a similar outcome.
If we don't write version-specific logic when using Java8 APIs, we would be shown a warning by Android Studio. The builds won't fail but the app will crash in the older versions.
Because of this lack of official support till now, we had to rely on multiple third-party libraries to avoid writing version specific logic.
Solution
To enable the official support in Android Studio, first thing to make sure is that we are using Android Studio version greater than 4.0.0.
Incase, we have set out minSdkVersion
to less than 20, we need to add Multi Dex Support, otherwise this would be enabled by default.
Next, enable desugaring by adding coreLibraryDesugaringEnabled
flag in compileOptions
.
compileOptions {
// Additional Java 8 APIs (Streams, Java Time) Support
coreLibraryDesugaringEnabled true
// Existing Java 8 (Lambda Expressions) Support
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
Finally, add the following dependency to the dependency block in the module level build.gradle
file.
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.9'
Once added, the errors and crashes with code below would be resolved. This makes version specific logic, like we have mentioned in the early part of the article redundant.val country = countryArrayList.stream().filter { it == "India" }.findFirst()
if (country.isPresent) {
// Country is present in the list
} else {
// Country is not present in the list
}
The findFirst
method returns Optional.empty
if the country specified in the filter is not present in the list. Otherwise, it will return the item as an option, Optional[India]
.
There are other ways we can find whether the item is present or not and how many times it got repeated but this is the way we have explored in the article.
What is Happening
On older versions, the library is simply compiling missing APIs to a separate dex file and is adding it to the APK. In addition to that, the library also rewrites our app's code to use this library instead during the runtime.
We have only discussed support for one API. For a detailed list of supported Java8 APIs, take a look at the Developer website.
Let's hope in the future releases of Android Studio, this Desugaring is added automatically and we don't have to add it manually.