A Quick Look to Kotlin DSL (KTS) from the Android Development Perspective

Entry

For a long time, I started to see that Kotlin DSL is preferred instead of traditional Groovy Scripts in Android Gradle build scripts by community. I have decided to learn more about Kotlin DSL, what are the benefits and harms of Kotlin DSL instead of Groovy? Should we convert existing projects build scripts from Groovy to KTS? Should we start to use Kotlin DSL for new projects? etc. So, in this article, I will summarize my findings and share my idea with you. Let’s start with the basics

For many years, we have been using Groovy to create build scripts for our Android Projects. You know that even if you don’t have complex build systems, at least, you have to manage your dependencies right? So if you are an Android Developer, you must be write something like that;

build.gradle
1
2
3
4
5
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.6.0'
}

Because this is the part of the default dependency code block from Android Studio :) The above code block exists in every default created application-level build.gradle file. And yes this file contains part of the entire build script in Groovy Script Language.

Now (actually after Gradle 5.0), Gradle has started to support Kotlin DSL in building scripts. The short meaning of this is that you can write build scripts with the power of Kotlin for your projects. When you hear this for the first time, you may feel excited but don’t be hurry for that because as all of us know;

The grass looks greener on the other side.

Advantages

  1. Language is Kotlin and we all know Kotlin better than Groovy,
  2. It has a content assist and refactoring abilities thanks IDEs which supports Kotlin DSL
  3. Partially type safety.
  4. Easy to migrate from Groovy to Kotlin DSL.

Disadvantages

  1. It has poor performance then Groovy scripts :) Yes, unfortunately, this is real and actually, this is not a surprise. Because almost all documents mentioned this; gradle, android, and there is open ticket for this also.
  2. If you create buildSrc along with KTS files, editing anything inside the buildSrc module cause Gradle to clear build caches. It means there will be a long build duration for you :) At that point, the architecture of buildSrc comes as important, you have to avoid writing frequently changed parts of building codes inside buildSrc.
  3. Not easy to migrate from Groovy to Kotlin DSL. (I know what I write, but sorry that is true, it depends on the project)

After all these things about advantages and disadvantages, if you ask me what you feel about Kotlin DSL, I can say that; yeah it is pretty good, but who wants to increase build time of projects even for 1 sec? So, I decided to try and compare performances for my android template projects. I will share results in this article but first let’s checks what changes how can we migrate our projects?

Migration

The first thing which feels good is that you can keep groovy and KTS files at the same time in your projects. You don’t have to change all Gradle build files at one time, you can start small ones or small modules to see how is it going. But I did not do with this way :)

Step 1: Creating buildSrc folder (optional)

Create a folder with the name “buildSrc” in the main folder of projects. Now, you can create build.gradle file inside this folder again. Then put the above code block inside it. These code blocks will be activated Kotlin DSL for buildSrc module.

build.gradle
1
2
3
4
5
6
7
8
9
import org.gradle.kotlin.dsl.`kotlin-dsl`

plugins {
`kotlin-dsl`
}

repositories {
mavenCentral()
}

Step 2: Preparing kotlin files in buildSrc (optional)

Now, you can create any folders, files, objects, or classes inside the buildSrc module to use them in build scripts. But do not forget that; when you change any file inside buildSrc, build caches will be removed and rebuilt from the beginning.

Step 3: Converting existing gradle files from groovy to KTS

Step 3.1: Script Names

First of all, if you write scripts with Kotlin DSL you have to change the file name from “build.gradle” to “build.gradle.kts” this refers Gradle script will be written with Kotlin Script. IDE may warn you about script type, when you accept KTS option, most probably, after a short time, you will see red underlines at every line :) Do not be panic, keep calm and continue reading :)

Step 3.2: Check & Fixed Syntax Problems Between Groovy and KTS

There are some differences between Groovy and KTS of course, we have to fix and understand them. I will try to write item by item;

String quotes: You have to change all string quotes from ‘ to “. As you know in Kotlin Syntax, ‘ can be used only char type and Unicode.

Assignments: If you assign and property without =, you have to write = operator in KTS.

Functions: You should call functions with (), if you don’t use parentheses you have to write them now.
Lets check these 3 things in an examples;

Example - 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# Groovy Syntax
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
id 'kotlin-parcelize'
id 'dagger.hilt.android.plugin'
}

# KTS Syntax
plugins {
id("com.android.library")
id("kotlin-android")
id("kotlin-kapt")
id("kotlin-parcelize")
id("dagger.hilt.android.plugin")
}

Example - 2

1
2
3
4
5
6
7
8
9
10
11
12

# Groovy Syntax
android {
compileSdk 31
buildToolsVersion '30.0.3'
...
# KTS Syntax
android {
compileSdk = 31
buildToolsVersion = "30.0.3"
...

Step 3.3: Convert Gradle Build Script Blocks

Depending on the complexity of the project there may be many script blocks and features inside your Gradle files, I will show how to convert commonly used operations in build scripts.

1. Product Flavors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# Groovy Syntax

flavorDimensions 'default'
productFlavors {
dev {
dimension 'default'
buildConfigField("String", "BASE_URL", "DEV_URL")
}
live {
dimension 'default'
buildConfigField("String", "BASE_URL", "LIVE_URL")
}
}

# KTS Syntax

flavorDimensions.add("default")
productFlavors {
create("dev") {
dimension = "default"

buildConfigField("String", "BASE_URL", "DEV_URL")
}

create("live") {
dimension = "default"

buildConfigField("String", "BASE_URL", "LIVE_URL")
}
}

2. Read From Property File

1
2
3
4
5
6
7
8
9
10
11
12

# Groovy Syntax

Properties appProperties = new Properties()
appProperties.load(project.rootProject.file("app.properties").newDataInputStream())

# KTS Syntax

val appProperties = Properties().apply {
load(FileInputStream(File(rootProject.rootDir, "app.properties")))
}

3. SignIn Configurations

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# Groovy Syntax

signingConfigs {
debug {
storeFile file("../keystore/android_template_debug.jks")
storePassword "androidtemplate"
keyAlias "template"
keyPassword "androidtemplate"
}
release {
storeFile file("../keystore/android_template_release.jks")
storePassword localProperties.get("release.key.storePassword")
keyAlias localProperties.get("release.key.alias")
keyPassword localProperties.get("release.key.password")
}
}

# KTS Syntax

signingConfigs {
getByName("debug") {
storeFile = file("../keystore/android_template_debug.jks")
storePassword = "androidtemplate"
keyAlias = "template"
keyPassword = "androidtemplate"
}
create("release") {
storeFile = file("../keystore/android_template_release.jks")
storePassword = localProperties["release.key.storePassword"] as String
keyAlias = localProperties["release.key.alias"] as String
keyPassword = localProperties["release.key.password"] as String
}
}


4. Tasks

1
2
3
4
5
6
7
8
9
10
11
# Groovy Syntax
task clean(type: Delete) {
delete rootProject.buildDir
}
# KTS Syntax
/**
* Default cleaning task definition
*/
tasks.register("clean", Delete::class) {
delete(rootProject.buildDir)
}

5. Dependencies

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# Groovy Syntax
appDependencies.gson,
project(":common:core"),
kapt(kaptDependencies.hiltAndroidCompiler)

liveApi(appDependencies.chucker)
devApi(debugDependencies.chucker)

# KTS Syntax

implementation(Dependencies.gson)
implementation(project(":common:core"))
kapt(KaptDependencies.hiltAndroidCompiler)

"liveApi"(Dependencies.chucker)
"devApi"(Dependencies.chucker)

6. Functions

1
2
3
4
5
6
7
8
9
10
11
12

# Groovy Syntax

def getDateTime() {
def date = new Date()
def formattedDate = date.format('dd-MM HH:mm', TimeZone.getTimeZone("GMT+3"))
return formattedDate
}

# KTS Syntax

fun getDateTime() = DateTimeFormatter.ofPattern("dd-MM HH:mm").format(LocalDateTime.now())

7. Manifest Placeholders

1
2
3
4
5
6
7
8
9
10
11
12
13

# Groovy Syntax

manifestPlaceholders = [
appIcon : "@mipmap/ic_launcher",
appIconRound: "@mipmap/ic_launcher_round"
]

# KTS Syntax

manifestPlaceholders["appIcon"] = "@mipmap/ic_launcher"
manifestPlaceholders["appIconRound"] = "@mipmap/ic_launcher_round"

Performance

So let’s come to what is the cost of all these good thins :)

I collect build times of same application with groovy build scripts and KTS build scripts. Here are the results;

Invalidate Cache & Restart:

Groovy: 27591 ms, KTS: 30326 ms -> average in 5 builds…

Changed Property in App Level Build Gradle Script

Groovy: 16515 ms, KTS: 22462 ms

Add New Dependency to App Level Build Gradle Script

Groovy: 16573 ms, KTS: 57362 ms -> (KTS dependency defined in buildSrc)

Result

In a summary, Kotlin DSL provides more efficient management during active coding and maintaining of build scripts however situation is totally opposite when building applications.

In addition, Android Studio still doesn’t create a build script with Kotlin DSL as default or there are not any options during creating a new project. They may wait for more improvements in the building performance of projects. Because performance issues are strongly underlined in Android and Gradle documents and still there is an open ticket in the Gradle project for the performance of Kotlin DSL.

I am thinking that this research experience was really good for me to learn what provides Kotlin DSL in near feature to Android Developers. Even though I could not decide to convert build scripts of an existing complex project into production, absolutely I will start to use Kotlin DSL in my new basic applications and experimental projects of course. Because I believe that performance of Kotlin DSL will be so close in the near feature and I want to be ready when it is ready totally :)

I hope you enjoyed it.

See you on the next topic.