Android App Development

Jetpack Compose: Migration to Gradle Kotlin DSL

March 21, 2022

Let’s explain using gradle kotlin dsl with buildSrc.

In this blog, we will learn how to migrate our Android application project from a Groovy-based Gradle system to a Kotlin-based Gradle system with the help of Kotlin DSL.

Introduction

Different ways of managing Gradle dependencies:

  1. Manual Management
  2. Google’s Recommendation using “ext”
  3. Kotlin DSL+ buildSrc

We will apply Kotlin DSL + buildSrc step in our projects.

What is buildSrc

buildSrc is a directory at the project root level which contains build info. We can use this directory to enable kotlin-dsl and write logic related to custom configuration and share them across the project. It was one of the most used approaches in recent days because of its testability.

The directory buildSrc is treated as an included build. Upon discovery of the directory, Gradle automatically compiles and tests this code and puts it in the classpath of your build script. For multi-project builds, there can be only one buildSrc directory, which has to sit in the root project directory. buildSrc should be preferred over script plugins as it is easier to maintain, refactor and test the code — Gradle Docs

as the docs say, in multi-module projects, there is only one buildSrc module and all modules can access it since it is placed in the classpath of build script, seems like a good place to put our Gradle logics to be reused.

Why buildSrc — Kotlin DSL

  • Compile-Time errors checking
  • Auto Completion and content assists
  • Friendly code navigation
  • Quick documentation (Kotlin function based)
  • Consistency of using the same language across the application
  • Enhanced IDE editing experience

Create buildSrc Kotlin Module

  • The First Step, Create Kotlin Library module name it buildSrc.
  • After selecting Finish you will get the following error.

FAILURE: Build failed with an exception.

* What went wrong:
‘buildSrc’ cannot be used as a project name as it is a reserved name

  • Remove include ‘:buildSrc’ in settings.gradle.
  • Convert the file name from build.gradle to build.gradle.kts in buildSrc folder.
  • After selecting Refactor, enable the option of ’kotlin-dsl’ in build.gradle.kts

buildSrc/build.gradle.kts

plugins {
`kotlin-dsl`
}repositories {
google()
mavenCentral()
gradlePluginPortal()
}
  • Click on sync now to finish the set-up.

Convert the settings.gradle

  • Convert the file name from settings.gradle to settings.gradle.kts. After that, if you try to compile, errors as below will show.
  • You need to change include ’:app’ to include(":app")

That’s it. You’re done for settings.gradle.kts. You can compile the project and it works with other Gradle files.

Convert the root project build.gradle

  • Same as the first file, convert the build.gradle to build.gradle.kts, using the steps provided above. Now you need to fix the syntax accordingly.

Before

After

Convert the App Module’s build.gradle

Finally, the last file to convert to Kotlin, which has the most syntax change.

But before that, let’s convert the build.gradle to build.gradle.kts first, using the steps provided above.

Now, we can continue to convert the syntax as following.

  • plugin code block
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}

to the below

plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
  • android code block
android {
compileSdk 32

defaultConfig {
applicationId "com.developersancho.migrationgradledsl"
minSdk 23
targetSdk 32
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}

to the below

android {
compileSdk = 32

defaultConfig {
applicationId = "com.developersancho.migrationgradledsl"
minSdk = 23
targetSdk = 32
versionCode = 1
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = rootProject.extra["compose_version"] as String
}
packagingOptions {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
  • dependencies code block
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
implementation 'androidx.activity:activity-compose:1.4.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
}

to the below

dependencies {
implementation("androidx.core:core-ktx:1.7.0")
implementation("androidx.compose.ui:ui:${rootProject.extra["compose_version"]}")
implementation("androidx.compose.material:material:${rootProject.extra["compose_version"]}")
implementation("androidx.compose.ui:ui-tooling-preview:${rootProject.extra["compose_version"]}")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.4.1")
implementation("androidx.activity:activity-compose:1.4.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.3")
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
androidTestImplementation("androidx.compose.ui:ui-test-junit4:${rootProject.extra["compose_version"]}")
debugImplementation("androidx.compose.ui:ui-tooling:${rootProject.extra["compose_version"]}")
debugImplementation("androidx.compose.ui:ui-test-manifest:${rootProject.extra["compose_version"]}")
}

Now we done. All the Gradle file is now converted to Kotlin.

Conclusion

I highly recommend the “Kotlin DSL + buildSrc” option. It may not seem like it’s that big of a deal, but managing Gradle dependencies is a pain, and having autocomplete and click support is a game changer. No more switching back and forth between files manually.

Happy and healthy coding!

Author: Mesut Genç

Tags

Android App Development Gradle Kotlin DSL Jetpack Compose Kotlin