Blog

Unlock the Future: Migrating Your Android App to Kotlin 2.0

On Friday, Nov 15, 2024
post image

Staying up-to-date with the latest technologies is crucial for Android developers to build innovative and efficient applications. Kotlin, the modern and expressive programming language, has been rapidly gaining traction in the Android ecosystem, and the release of Kotlin 2.0 brings even more exciting features and improvements.

If you’ve been hesitant to leap to Kotlin 2.0, now is the perfect time to embark on this transformative journey. In this post, we’ll explore the key steps involved in migrating your existing Android project from previous Kotlin versions to cutting-edge Kotlin 2.0.

But before we dive into the migration process, let’s take a closer look at the core features and highlights of Kotlin 2.0:

Kotlin 2.0 Highlights

  • New Compose Compiler Gradle Plugin: The Jetpack Compose compiler has been integrated into the Kotlin repository and upgraded to version 2.0.0. To use it, developers need to apply the org.jetbrains.kotlin.plugin.compose Gradle plugin to match their Kotlin 2.0.0 version. This change ensures better compatibility between Compose and Kotlin 2.0.0 since they’ll now be released together.
  • Lambda Function Generation using invokedynamic: In Kotlin 2.0.0, lambda functions are now generated using invokedynamic by default, replacing the previous anonymous class approach. This new method produces smaller binary sizes and better aligns with JVM optimizations. However, it has three limitations: lambdas aren’t serializable, don’t support the experimental reflect() API, and produce less readable toString() output. Developers can revert to the old behavior either by using the @JvmSerializableLambda annotation for specific lambdas or the -Xlambdas=class compiler option for an entire module.
  • Stable kotlinx-metadata-jvm Library: The kotlinx-metadata-jvm library has been promoted to Stable in Kotlin 2.0.0 and renamed to kotlin-metadata-jvm. It’s now integrated into the main Kotlin release cycle with the same backward compatibility guarantees as the standard library. The library allows developers to read and modify metadata from Kotlin/JVM compiler-generated binary files.
  • Support for Named Export in Kotlin/Wasm: Kotlin/Wasm now supports named exports, allowing JavaScript code to import specific Kotlin functions marked with @JsExport by name, instead of accessing them through a default module export. This improvement makes code sharing between Kotlin and JavaScript more straightforward and enhances module dependency management and readability.

And much more. If you want to check all the changes in the Kotlin 2.0 version, please check the release notes here.

The K2 Compiler: A Powerful Foundation

At the heart of Kotlin 2.0 is the new K2 compiler, which has undergone an extensive stabilization process. The K2 compiler brings major performance improvements, speeds up new language feature development, unifies all platforms that Kotlin supports, and provides a better architecture for multiplatform projects.

Now that we’ve explored the exciting features and the powerful foundation of Kotlin 2.0, let’s dive into the key steps to migrate your existing Android project to this cutting-edge version.

Updating the Kotlin Version

The first step to migrate to Kotlin 2.0 is to update the Kotlin version of your project. It’s important to ensure that all the libraries used in your project are also up to date to avoid any incompatibilities.

Assuming that we are using Gradle Version Catalogs (If you are not using it, check this blog for more information), let’s modify the kotlin version in the libs.versions.toml file. Locate the Kotlin version references and update them to the latest Kotlin 2.0 version. For example, you might have the following:


[versions]
androidGradlePlugin = "8.1.4"
kotlin = "2.0.0" # Updated 🔥
coreKtx = "1.15.0"
# More versions...

[libraries]
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
# More libraries ...

[plugins]
android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
# More plugins

After updating the Kotlin version and syncing the Gradle dependencies, it is possible that a warning will come up recommending scanning the project to detect deprecated features or libraries.

It’s recommended to remove deprecated features to avoid crashes in the future.

Managing the New Build Folder

Kotlin 2.0 introduces a new build folder that must be excluded from your project’s repository. This new folder contains temporary files generated during the build process and should not be included in version control.

Previously, all the compilation files generated were added to the .gradle folder now, a specific folder named .kotlin is added to manage the Kotlin-exclusive compilation files.

Just update the .gitignore file to add this new folder

# Located in the project root folder
/.kotlin

Configuring Jetpack Compose

Previously, managing Jetpack Compose versions was a significant challenge for Android developers. The process required careful synchronization between Kotlin updates and the Compose compiler version. Developers often found themselves in a waiting game - unable to adopt the latest Kotlin features until a compatible Compose compiler version was released. This dependency dance not only slowed down development but also created unnecessary complexity in project maintenance.

One of the most notable changes in Kotlin 2.0 is how we configure the Compose compiler. In previous versions, we had to explicitly specify the compiler version in the build.gradle.kts file at the app level. The configuration looked something like this:


android {
    buildFeatures {
        compose = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.4"
    }
}

Now, with Kotlin 2.0, this friction has been eliminated. The Compose compiler is integrated directly into the Kotlin compiler, streamlining the entire process and removing the need for version synchronization. Instead, we need to specify the compiler version through a Gradle plugin:

  1. Set the plugin in the libs.versions.toml file:

[versions]
kotlin = "2.0.0" # Updated 🔥

[plugins]
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin"
  1. After synchronizing the project, let’s apply the plugin in the build.gradle.kts file at project and app level:
  • Project level

    
    plugins {
        alias(libs.plugins.compose.compiler) apply false
    }
    

  • App level

    
    plugins {
        alias(libs.plugins.compose.compiler)
    }
    

And let’s remove the previous configuration where we indicated the compiler version to use in the build.gradle.kts file at the app level:


android {
    buildFeatures {
        compose = true
    }
    // Compose options removed
}

Additionally, a new configuration block was added to enhance and adjust the compose compiler easily, avoiding the use of compilation arguments and setting up on the build.gradle.kts file directly. let’s see an example:


android {
    buildFeatures {
        compose = true
    }

    composeCompiler { // Configuration block
        enableStrongSkippingMode = true
    }
}

This new approach offers several advantages:

  • Direct alignment with your Kotlin version
  • Simplified version management through version catalogs
  • Reduced risk of version compatibility issues
  • Centralized configuration in your project’s settings

The integration between Kotlin and the Compose compiler means you can now confidently update your Kotlin version, knowing that the Compose compiler will automatically stay in sync. This represents a significant improvement in the developer experience, making it easier to maintain and update your Android projects.

Aligning KSP Configuration

If your Android project utilizes Kotlin Symbol Processing (KSP), it’s crucial to ensure that the KSP version is aligned with the new Kotlin 2.0 version. KSP is a powerful tool that enables efficient processing of annotations in Kotlin projects.

Many popular Android libraries, such as Room, Dagger Hilt, Moshi, and Data Binding, rely on annotations to generate code for tasks like database access, dependency injection, and JSON parsing. In Kotlin, we need specialized tools that can effectively process these annotations, and that’s where KSP comes into play.

To ensure a smooth migration to Kotlin 2.0, you’ll need to update the KSP configuration in your libs.versions.toml file. Locate the KSP-related configuration and update the version numbers to match the Kotlin 2.0 version you’re using. For example:


[version]
ksp = "2.0.20-1.0.25" # Referenced version to match with Kotlin version 2.0.0

[plugin]
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }

Let’s apply the plugin in the build.gradle.kts file at project and app level and use it with the dependency needed:

  • Project level

    
    plugins {
        alias(libs.plugins.ksp) apply false
    }
    

  • App level

    
    plugins {
        alias(libs.plugins.ksp)
    }
    
    android{
        // ...
    }
    
    dependencies {
        implementation(libs.androidx.room.runtime)
        ksp(libs.androidx.room.compiler)
        implementation(libs.androidx.room.ktx)
    }
    

By keeping your KSP configuration up-to-date, you’ll be able to take full advantage of the improvements and features introduced in Kotlin 2.0, further enhancing the productivity and efficiency of your Android development workflows.

Conclusion

Migrating to Kotlin 2.0 represents a decisive step for Android developers aiming to keep their projects modern, efficient, and scalable. With its host of new features, Kotlin 2.0 not only streamlines the development process but also paves the way for seamless integration with key Android technologies like Jetpack Compose and Kotlin Symbol Processing (KSP). By updating dependencies, configuring new tools, and adopting the K2 compiler, you are setting your project up for enhanced performance and better future compatibility.

Taking the time to follow best practices for migration ensures that your codebase is prepared for what’s ahead in Android development. With Kotlin’s improved compiler and integrations, you’ll enjoy quicker build times, simplified configuration, and greater flexibility across platforms.

References

Share this post: