Switch from Gradle Groovy to Kotlin DSL: Android

Suneet Srivastava
OYE! Engineering
Published in
5 min readJul 16, 2021
Dependency Management

The new way to manage dependencies in android using Kotlin DSL.

Pros:
1. Kotlin First
2. Making hard un-understandable groovy syntax file to easy kotlin syntax file.
3. Helps in managing dependency and their versions easily in multi-module project.

Cons:
1. Slow build time as compared to groovy

Let’s get started:

  1. On the root project, add a new package buildSrc
  2. Next add a new file with in the build.src package — name build.gradle.kts and add the following kotlin-dslplugin.
import org.gradle.kotlin.dsl.`kotlin-dsl`

plugins {
`kotlin-dsl`
}

repositories {
jcenter()
}

Now click on Sync now and let the project build successfully.

Next steps now,
1. Create a package hierarchy with src/main/java

2. Now create a file that will have the versions and dependencies. — I am naming it as Deps.kt.The structure would look like

3. Rename settings.gradle to settings.gradle.kts

include ‘:app’

This will be changed to

include (":app")
//If there are more than one module, keep them comma seperated

4. Rename build.gradle (project-level) to build.gradle.kts and refactor the file like below build.gradle.kts

buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath ("com.android.tools.build:gradle:${Versions.GradleVersion}")
classpath ("org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.KotlinVersion}")
classpath ("com.google.gms:google-services:4.3.8")
classpath ("com.google.firebase:perf-plugin:1.4.0")
classpath ("com.google.firebase:firebase-crashlytics-gradle:2.6.1")
classpath ("com.google.dagger:hilt-android-gradle-plugin:${Versions.HiltVersion}")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
google()
jcenter()
mavenCentral()
maven ("https://maven.google.com")
maven ("https://jitpack.io")
flatDir {
dirs ("libs")
}
}
}

tasks.register("clean", Delete::class) {
delete (rootProject.buildDir)
}
//If you have multiple task follow the same way like clean tasks is done. You can give a read at https://docs.gradle.org/current/userguide/migrating_from_groovy_to_kotlin_dsl.html

Here you see the Versions are coming from Deps file which we created under buildSrc. So the Deps file looks like —

object Versions {
const val KotlinVersion = "1.4.32"
const val HiltVersion = "2.35.1"
const val GradleVersion = "4.1.3"
}

5. Now comes the longest file to refactor — the app level build.gradle . Change the build.gradle(app)levelfile to build.gradle.kts

  • Refactor the plugins block as
plugins {
id("com.android.application")
id("com.google.firebase.crashlytics")
id("com.google.firebase.firebase-perf")
kotlin("android")
kotlin("kapt")
id("dagger.hilt.android.plugin")
}

Define an object AppConfig in the Deps.kt file, add/remove the ones which are fitting to your requirements

object AppConfig {
const val TestInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
const val CompileSdkVersion = 30
const val MinSdkVersion = 21
const val TargetSdkVersion = 30
const val VersionCode = 1
const val VersionName = "1.1.0"
}
  • Now under the android block perform the refactoring like below
android {
compileSdkVersion(AppConfig.CompileSdkVersion)
defaultConfig {
multiDexEnabled = true
applicationId = "com.codedsun"
minSdkVersion (AppConfig.MinSdkVersion)
targetSdkVersion(AppConfig.TargetSdkVersion)
versionCode = AppConfig.VersionCode
versionName = AppConfig.VersionName
testInstrumentationRunner = AppConfig.TestInstrumentationRunner
vectorDrawables.useSupportLibrary = true
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

buildFeatures {
viewBinding = true
}

//...File contains more lines

}

→ Now if you have multiple buildtypes, define it as below inside the android block. You have to use getByName (“release”) or (“debug”)

getByName("release") {
minifyEnabled(true)
isShrinkResources = true
zipAlignEnabled(true)
android.defaultConfig.vectorDrawables.useSupportLibrary = true
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
}

Important Part: Not easily available on the internet

Jokes apart, let’s dive in below

If you use buildConfig fields inside the build types, you can define it as below

buildConfigField ("String", "BuildName", "app-release")

The challenge is — if the buildConfig fields are defined under gradle.properties file.

under buildTypes define :

buildTypes {
val BuildName: String by project //the property defined in gradle.properties
getByName("release") {
buildConfigField ("String", "BuildName", BuildName)

...
buildConfigField("Boolean/String/Long", "BuildConfigField", "BuildConfigValue"

}

Make sure the variable BuildName (as it’s) exists in gradle.properties file

One thing to learn from here is that buildConfigField function — takes 3 params :

type: String — The data type of the field you want to assign to the build config field. Here you can define your values as Boolean, Long, Int..etc
name: String — the name of the buildConfig field
value: String — the value of the buildConfig field which has to be in the string only, it doesn’t have to be defined as the same that you defined in the type. Example bold-marked above

  • Now comes the dependencies block
implementation fileTree(dir: “libs”, include: [“*.jar”])

will be changed to

implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar")

If you have aar’s file also in the project, you don't have to write separately for every file

implementation('libs-1.3.0',ext:'aar') //This is wrong

Instead, include the .aar in the line which has .jar files implementation

implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
  • Now inside the Deps.kt file, make an object Deps and define the versions in the Versions object
object Versions {
const val KotlinVersion = "1.4.32"
const val HiltVersion = "2.35.1"
const val GradleVersion = "4.1.3"
const val CoroutinesVersion = "1.4.3"
const val AppCompat = "1.3.0"
const val ConstraintLayout = "2.0.4"
const val Material = "1.1.0-beta01"
const val Browser = "1.3.0"
const val RecyclerView = "1.2.1"
const val LifecycleExtensions = "2.2.0"
const val ViewModel = "2.3.1"
}
object Deps {
const val KotlinSdkLib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Versions.KotlinVersion}"
const val AppCompat = "androidx.appcompat:appcompat:${Versions.AppCompat}"
const val ConstraintLayout = "androidx.constraintlayout:constraintlayout:${Versions.ConstraintLayout}"
const val Material = "com.google.android.material:material:${Versions.Material}"
const val Browser = "androidx.browser:browser:${Versions.Browser}"
const val RecyclerView = "androidx.recyclerview:recyclerview:${Versions.RecyclerView}"
const val LifecycleExtensions = "androidx.lifecycle:lifecycle-extensions:${Versions.LifecycleExtensions}"
const val ViewModel = "androidx.lifecycle:lifecycle-viewmodel:${Versions.ViewModel}"

Hence the dependencies block would look like

dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
implementation(Deps.KotlinSdkLib)
implementation(Deps.AppCompat)
implementation(Deps.ConstraintLayout)
implementation(Deps.Material)
implementation(Deps.Browser)
implementation(Deps.RecyclerView)
implementation(Deps.LifecycleExtensions)
implementation(Deps.ViewModel)
}

If you have more dependencies, follow the same process to define the dependencies and you are good to go.

Thanks for hanging in for so-long 😎. Do share your 👏 or comments below.

We are all about building products that resolve world-class problems, so if you are looking to challenge yourself, join our team at Oye! Rickshaw. We are hiring. Check for open positions.

--

--