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.

java-8-api-error

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.

Popular posts from this blog

Add Spacing to Recycler View Linear Layout Manager Using Item Decoration

How to Change Material Chip Text Size, Text Style and Font

How to Read Metadata from AndriodManifest File

How to Set an Android App as a Default Dialer

Use Kotlin Android Extensions to Replace findViewById