Android — Migrating to Kotlin Script

Rudbeckia hirta

Introduction

Android Experiment

So for the first part of our experiment, I’m going to give an overview of how I set up Kotlin Script in an Android project, for more details please take a look at these references:

Documentation: https://bit.ly/2TjDEwO
The New Way of Writing Build Gradle with Kotlin DSL: https://bit.ly/388T1fs
I hated Gradle! Kotlin and the buildSrc Plugin made me love it: https://bit.ly/2tlMM9B

Github Project link:

Disclaimer: I used the Android Studio 4.0 Preview for this project to use the Jetpack Compose.

Kotlin Script

The “amazing” buildSrc

Setup

  • settings.gradle ===> settings.gradle.kts
  • build.gradle ===> build.gradle.kts

Settings Gradle

Settings Gradle (Groovy)
Settings Gradle (Kotlin Script)

The settings Gradle file is not that different, the main difference is that when using Kotlin Script we must replace all single quote signs (‘) by double quote signs (“) and the function calls must use parenthesis.

Top-Level Build Gradle

Top-Level Build Gradle (Groovy)
Top-Level Build Gradle (Kotlin Script)

So, let’s point the differences between these Gradle files. The first thing is that the maven call is an extension function declared on RepositoryHandlerExtensions for the RepositoryHandler Java Interface

fun RepositoryHandler.maven(url: Any) =
maven { it.setUrl(url) }

Also, the classpath must be defined as a function, since it’s using an extension function declared on ScriptHandlerScope class for the DependencyHandler

fun DependencyHandler.classpath(dependencyNotation: Any): Dependency? =
add(CLASSPATH_CONFIGURATION, dependencyNotation)

Another interesting point is that we can use the kotlin function on KotlinDependencyExtensions to declare dependencies defined under “org.jetbrains.kotlin:kotlin-”

fun DependencyHandler.kotlin(module: String, version: String? = null): Any =
"org.jetbrains.kotlin:kotlin-$module${version?.let { ":$version" } ?: ""}"

Finally, the clean task definition, in case of Kotlin Script we use the register extension function from the tasks declared on ProjectDelegate class

inline fun <T : org.gradle.api.Task> org.gradle.api.tasks.TaskContainer.`register`(`name`: String, `type`: kotlin.reflect.KClass<T>, `configurationAction`: org.gradle.api.Action<in T>): org.gradle.api.tasks.TaskProvider<T> =
`register`(`name`, `type`.java, `configurationAction`)

Application Build Gradle

Application Build Gradle (Groovy)
Application Build Gradle (Kotlin Script)

Despite the basic modifications like swapping quotation marks and call functions using parenthesis (In case of attributes must use the equal sign, it’s hard to know which are functions and which are attributes, I did it empirically), we must apply the following changes:

  • Apply plugins using the plugins closure, it’s possible to declare plugins using convention objects, but according to the Gradle documentation (https://bit.ly/2tjJIKX):

Please avoid using convention objects when writing new plugins. The long term plan is to migrate all Gradle core plugins to use extensions and remove the convention objects altogether.

  • The Build types declaration which needs to use the getByName function to retrieve types according to the build type name provided
  • The dynamicFeatures attribution, which needs to declare a mutable set to assign
  • For the dependencies, I declared a separate kotlin file and split into api, implementation, testImplementation and androidTestImplementation. These dependencies are defined under buildSrc folder (we could do the same by applying Groovy Gradle files).
AppDependencies
Dependencies
Versions

After all these modifications you will be able to build your project using Kotlin Script 🎉 I hope it helped you and see you next time.

Android Engineer @Triller