Posted by: Satya Pavan Kantamani
Change your Android build scripts to Kotlin DSL for better flexibility
Introduction
In Android Studio the build Gradle files in our project structure by default use Groovy build language. We basically define dependencies, plugins, project settings, etc in our Gradle files. As we are busy with our development tasks and timelines we don’t take space to know more about them. And writing code in these Gradle files doesn’t seem excited because we are not well versed with Groovy.
In this post let’s explore What is a DSL and the Kotlin DSL migration journey from Groovy DSL.
What is a DSL?
DSL is an acronym for Domain Specific Language that can be used in the context of a particular domain. It’s a contrast to General-Purpose Language (GPL) like Java which is widely applicable or used for multiple domains. It helps us to write declarative code to reduce the boilerplate stuff. The code written with DSL would be much easier to read.
The common usage of DSL language is HTML in Web development, Gradlein build tools, SQL in data management, XML for the Markup language, etc. Though we might have experience in above mentioned one or more languages but we might not know that we are using DSL.
Gradle
Gradle is a powerful build tool. Gradle provides a domain-specific language, or DSL, for describing builds scripts. Gradle provides support for Groovy and Kotlin domain-specific language or DSL, for describing builds. A Groovy build script can contain any Groovy language element. A Kotlin build script can contain any Kotlin language element.
As most of us are using Kotlin for development it would be easy and flexible to have build scripts in Kotlin rather than Groovy. Now let’s move to Kotlin DSL details.
Kotlin DSL
Kotlin DSL is built on top of the core language Kotlin. So the syntax would no different from the parent language which gives us the benefit of using Kotlin for development. Kotlin DSL is fully supported in Android Studio.
Gradle’s Kotlin DSL provides an alternative syntax to the traditional Groovy DSL with an enhanced editing experience in supported IDEs, with superior content assist, refactoring, documentation, and more — Gradle Team
We can have few benefits from choosing Kotlin DSL over Groovy
- Good readability
- Easily adaptable Kotlin syntax from the parent language
- Code Navigation and auto suggestions
- Compile-time time errors
- Currently supports type-safe model accessors for Dependency and artifact configurations, source sets, etc
But it might be a bit slower in some situations like clean, buildSrc directory related changes, etc
Migrating build scripts from Groovy to Kotlin
Before starting migration let’s go through some default things
- Groovy strings can be quoted with single quotes
'string'
or double quotes"string"
whereas Kotlin requires double quotes"string"
. - Groovy allows omitting parentheses when invoking functions whereas Kotlin always requires the parentheses.
- The Gradle Groovy DSL allows omitting the
=
assignment operator when assigning properties whereas Kotlin always requires the assignment operator.
Let’s get started with conversions. In this post let’s convert build.gradle and settings.gradle file to .gradle.kts
First of all rename .gradle
files to .gradle.kts
(Groovy DSL script files use the .gradle
file name extension & Kotlin DSL script files use the .gradle.kts
file name extension)
Step 1
Let’s refractor settings.gradle to settings.gradle.kts
//Before | |
include ':app', ':sampleModule' | |
//After | |
include(":app", ":sampleModule") |
Step 2
Now let’s migrate project-level build.gradle to build.gradle.kts. The default root or project-level build.gradle generated will be looking as below
// Top-level build file where you can add configuration options common to all sub-projects/modules. | |
buildscript { | |
ext.kotlin_version = "1.5.0" | |
repositories { | |
google() | |
mavenCentral() | |
} | |
dependencies { | |
classpath "com.android.tools.build:gradle:4.2.1" | |
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | |
// NOTE: Do not place your application dependencies here; they belong | |
// in the individual module build.gradle files | |
} | |
} | |
allprojects { | |
repositories { | |
google() | |
mavenCentral() | |
jcenter() // Warning: this repository is going to shut down soon | |
} | |
} | |
task clean(type: Delete) { | |
delete rootProject.buildDir | |
} |
As we observe the code initial part is variable decleration
ext.kotlin_version = "1.5.0"
which will be changed to
val kotlin_version = "1.5.0"
Skipping repositories part let’s move to the classpath. Specifying functions using classpath to add dependencies to the script
classpath "com.android.tools.build:gradle:4.2.1" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
But with Kotlin DSL specifying function would as following
classpath ("com.android.tools.build:gradle:4.2.1") classpath ("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
By applying the specified rules mentioned above the complete will be changed as below. Take a closer look to understand the differences.
// Top-level build file where you can add configuration options common to all sub-projects/modules. | |
buildscript { | |
val kotlin_version = "1.5.0" | |
repositories { | |
google() | |
mavenCentral() | |
} | |
dependencies { | |
classpath ("com.android.tools.build:gradle:4.2.1") | |
classpath ("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") | |
// NOTE: Do not place your application dependencies here; they belong | |
// in the individual module build.gradle files | |
} | |
} | |
allprojects { | |
repositories { | |
google() | |
mavenCentral() | |
jcenter() // Warning: this repository is going to shut down soon | |
} | |
} | |
tasks.register("clean", Delete::class){ | |
delete(rootProject.buildDir) | |
} |
As we are done with project-level gradle file let’s move to app-level gradle file
Hier noch einige Links
Job Offers
Step 3
Now let’s migrate app-level build.gradle to build.gradle.kts. The default app-level build.gradle generated will be looking as below
plugins { | |
id 'com.android.application' | |
id 'kotlin-android' | |
} | |
android { | |
compileSdkVersion 30 | |
buildToolsVersion "30.0.3" | |
defaultConfig { | |
applicationId "com.sample.myapplication" | |
minSdkVersion 21 | |
targetSdkVersion 30 | |
versionCode 1 | |
versionName "1.0" | |
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" | |
} | |
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' | |
} | |
} | |
dependencies { | |
implementation 'androidx.core:core-ktx:1.5.0' | |
implementation 'androidx.appcompat:appcompat:1.3.0' | |
implementation 'com.google.android.material:material:1.3.0' | |
implementation 'androidx.constraintlayout:constraintlayout:2.0.4' | |
testImplementation 'junit:junit:4.+' | |
androidTestImplementation 'androidx.test.ext:junit:1.1.2' | |
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' | |
} |
After migration it will be looking as:
plugins { | |
id("com.android.application") | |
kotlin("android") | |
} | |
android { | |
compileSdkVersion(30) | |
buildToolsVersion("30.0.3") | |
defaultConfig { | |
applicationId = "com.sample.dsl" | |
minSdkVersion(21) | |
targetSdkVersion(30) | |
versionCode = 1 | |
versionName = "1.0" | |
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" | |
} | |
buildTypes { | |
getByName("release") { | |
isMinifyEnabled = false | |
proguardFiles( | |
getDefaultProguardFile("proguard-android-optimize.txt"), | |
"proguard-rules.pro" | |
) | |
} | |
} | |
compileOptions { | |
sourceCompatibility = JavaVersion.VERSION_1_8 | |
targetCompatibility = JavaVersion.VERSION_1_8 | |
} | |
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> { | |
kotlinOptions { | |
jvmTarget = "1.8" | |
} | |
} | |
} | |
dependencies { | |
implementation("androidx.core:core-ktx:1.5.0") | |
implementation("androidx.appcompat:appcompat:1.3.0") | |
implementation("com.google.android.material:material:1.3.0") | |
implementation("androidx.constraintlayout:constraintlayout:2.0.4") | |
testImplementation("junit:junit:4.+") | |
androidTestImplementation("androidx.test.ext:junit:1.1.2") | |
androidTestImplementation("androidx.test.espresso:espresso-core:3.3.0") | |
} |
More or less both look similar but Kotlin files can give better readability. There would be minor changes
- Like for release type we need to use
getByName(“release”)
rather thanrelease
- And assignment of values like
versionCode = 1
rather thanversionCode 1
- there would be name changes for properties like
isMinifyEnabled
rather thanminifyEnabled
and few other
This post was just a start to understand about Kotlin DSL there are many more things that we can do. Let’s see much more in upcoming posts.
Summary
Kotlin DSL is comparatively easy and has good cons when compared with Groovy. Parent Kotlin language syntax will always be beneficial. I would recommend using Kotlin DSL though it’s a bit slower in some cases. It would in well readable format once you have migrated your build files. We can have interoperability with Groovy code but to some extent.
Let’s learn about better dependency management using buildSrc in Kotlin DSL in the next post. Don’t forget to clap if you found this article helpful.
References
Continue Reading Android Stuff
Understand How View Renders in Android
The Life Cycle of a View in Android
Tags: Android, AndroidDev, Programming, Kotlin
View original article at: