Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Improving your Android Gradle Experience with Kotlin

Improving your Android Gradle Experience with Kotlin

The talk was given at Kotlin Everywhere Hamburg, August 2019 - https://www.eventbrite.co.uk/e/kotlineverywhere-hamburg-tickets-64993039932

The talk involved sharing tips about how we can optimize Gradle for our Android projects. The talk shares some approaches using the standard Groovy DSL for customizing Gradle build system, and takes it into using Kotlin DSL and also writing custom Gradle plugins in Kotlin.

Resources:
* https://handstandsam.com/2019/03/12/sharing-gradle-configuration-in-multi-module-android-projects/
* https://segunfamisa.com/posts/android-gradle-extra-properties
* https://gradle.com/blog/getting-started-with-gradle-kotlin-dsl/
* https://github.com/gradle/kotlin-dsl
* Build Bigger, Better: Gradle for Large Projects (Google I/O'19) - https://www.youtube.com/watch?v=sQC9-Rj2yLI

Segun Famisa

August 30, 2019
Tweet

More Decks by Segun Famisa

Other Decks in Programming

Transcript

  1. View Slide

  2. @segunfamisa

    View Slide

  3. Outline
    • Introduction
    • Scenarios & improvements #0, #1, #2
    • Taking it a step further with Kotlin DSL
    • buildSrc
    • Migrating build scripts to Kotlin DSL from Groovy DSL
    • Kotlin DSL improvements
    • Custom Plugins
    • Resources

    View Slide

  4. Improving your gradle
    experience

    View Slide

  5. #0

    View Slide

  6. Small project, single or few modules
    #0

    View Slide

  7. // build.gradle
    apply plugin: 'com.android.application'
    ...
    dependencies {
    ...
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
    implementation "io.reactivex.rxjava2:rxjava:2.2.5"
    }
    #0

    View Slide

  8. // build.gradle
    apply plugin: 'kotlin'
    ...
    dependencies {
    ...
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
    implementation "io.reactivex.rxjava2:rxjava:2.2.5"
    }
    // build.gradle
    apply plugin: 'com.android.application'
    ...
    dependencies {
    ...
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
    implementation "io.reactivex.rxjava2:rxjava:2.2.5"
    }
    #0

    View Slide

  9. // build.gradle
    apply plugin: 'com.android.application'
    ...
    dependencies {
    ...
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
    implementation "io.reactivex.rxjava2:rxjava:2.2.5"
    }
    // build.gradle
    apply plugin: 'kotlin'
    ...
    dependencies {
    ...
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
    implementation "io.reactivex.rxjava2:rxjava:2.2.5"
    }
    #0

    View Slide

  10. #0
    // build.gradle
    apply plugin: 'com.android.application'
    ...
    dependencies {
    ...
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
    implementation "io.reactivex.rxjava2:rxjava:2.2.5"
    }
    // build.gradle
    apply plugin: 'kotlin'
    ...
    dependencies {
    ...
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
    implementation "io.reactivex.rxjava2:rxjava:2.2.5"
    }
    Managing dependencies versions becomes tedious

    View Slide

  11. #0 - solution

    View Slide

  12. #0 - solution
    Solving the duplication problem with Gradle extra (ext) properties feature

    View Slide

  13. // dependencies.gradle
    ext {
    ...
    libraries = [
    rxJavaVersion : '2.2.5'
    rxAndroidVersion : '2.1.0'
    kotlinVersion : '1.3.31'
    ]
    }
    1. Externalize dependencies

    View Slide

  14. 1. Externalize dependencies
    2. Apply dependencies.gradle to root project
    // root build.gradle
    buildScript {...}
    allprojects {...}
    apply from: 'relative/path/to/dependencies.gradle'

    View Slide

  15. 1. Externalize dependencies
    2. Apply dependencies.gradle to root project
    3. Reference the dependencies in the build.gradle files
    // build.gradle
    dependencies {
    ...
    def libs = rootProject.ext.libraries
    implementation "org.jetbrains.kotlin:...:$libs.kotlinVersion"
    implementation "io.reactivex.rxjava2:rxjava:$libs.rxJavaVersion"
    }

    View Slide

  16. 1. Externalize dependencies
    2. Apply dependencies.gradle to root project
    3. Reference the dependencies in the build.gradle files
    // build.gradle
    dependencies {
    implementation "org.jetbrains.kotlin:...:$libs.kotlinVersion"
    implementation "io.reactivex.rxjava2:rxjava:$libs.rxJavaVersion"
    }
    // build.gradle
    dependencies {
    ...
    def libs = rootProject.ext.libraries
    implementation "org.jetbrains.kotlin:...:$libs.kotlinVersion"
    implementation "io.reactivex.rxjava2:...:$libs.rxJavaVersion"
    }

    View Slide

  17. 1. Externalize dependencies
    2. Apply dependencies.gradle to root project
    3. Reference the dependencies in the build.gradle files
    // build.gradle
    dependencies {
    implementation "org.jetbrains.kotlin:...:$libs.kotlinVersion"
    implementation "io.reactivex.rxjava2:rxjava:$libs.rxJavaVersion"
    }
    // build.gradle
    dependencies {
    ...
    def libs = rootProject.ext.libraries
    implementation "org.jetbrains.kotlin:...:$libs.kotlinVersion"
    implementation "io.reactivex.rxjava2:...:$libs.rxJavaVersion"
    }

    View Slide

  18. • Managing/updating versions of dependencies is now easier - just
    need to change in one place
    • Still has the problem of duplication of dependencies

    View Slide

  19. #1

    View Slide

  20. #1
    • Fairly complex projects
    • Multiple modules
    • You will find a lot of repetitions in the
    dependencies

    View Slide

  21. #1
    // build.gradle
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
    testImplementation "junit:junit:$libs.jUnitVersion"
    ...
    // other dependencies
    }

    View Slide

  22. #1
    // build.gradle
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
    testImplementation "junit:junit:$libs.jUnitVersion"
    ...
    // other dependencies
    }
    // build.gradle
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
    testImplementation "junit:junit:$libs.jUnitVersion"
    ...
    // other dependencies
    }

    View Slide

  23. #1
    // build.gradle
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
    testImplementation "junit:junit:$libs.jUnitVersion"
    ...
    // other dependencies
    }
    // build.gradle
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
    testImplementation "junit:junit:$libs.jUnitVersion"
    ...
    // other dependencies
    }
    // build.gradle
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
    testImplementation "junit:junit:$libs.jUnitVersion"
    ...
    // other dependencies
    }

    View Slide

  24. #1
    // build.gradle
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
    testImplementation "junit:junit:$libs.jUnitVersion"
    ...
    // other dependencies
    }
    // build.gradle
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
    testImplementation "junit:junit:$libs.jUnitVersion"
    ...
    // other dependencies
    }
    // build.gradle
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
    testImplementation "junit:junit:$libs.jUnitVersion"
    ...
    // other dependencies
    }
    // build.gradle
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
    testImplementation "junit:junit:$libs.jUnitVersion"
    ...
    // other dependencies
    }

    View Slide

  25. #1
    // build.gradle
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
    testImplementation "junit:junit:$libs.jUnitVersion"
    ...
    // other dependencies
    }
    // build.gradle
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
    testImplementation "junit:junit:$libs.jUnitVersion"
    ...
    // other dependencies
    }
    // build.gradle
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
    testImplementation "junit:junit:$libs.jUnitVersion"
    ...
    // other dependencies
    }
    // build.gradle
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
    testImplementation "junit:junit:$libs.jUnitVersion"
    ...
    // other dependencies
    }
    Problem: Repeated declarations of dependencies in so many modules

    View Slide

  26. #1
    Solution: extract common/shared dependencies and apply to the modules

    View Slide

  27. 1. Extract common dependencies
    // shared-dependencies.gradle - contains all shared dependncies
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
    ...
    testImplementation 'junit:junit:4.12'
    }

    View Slide

  28. 1. Extract common dependencies
    2. Apply common dependencies to respective modules
    // build.gradle
    apply from: “relative/path/to/shared-dependencies.gradle"
    ...
    dependencies {
    ...
    // other dependencies
    }

    View Slide

  29. 1. Extract common dependencies
    2. Apply common dependencies to respective modules
    // build.gradle
    apply from: “relative/path/to/shared-dependencies.gradle"
    ...
    dependencies {
    ...
    // other dependencies
    }
    // build.gradle
    apply from: “relative/path/to/shared-dependencies.gradle"
    ...
    dependencies {
    ...
    // other dependencies
    }

    View Slide

  30. 1. Extract common dependencies
    2. Apply common dependencies to respective modules
    // build.gradle
    apply from: “relative/path/to/shared-dependencies.gradle"
    ...
    dependencies {
    ...
    // other dependencies
    }
    // build.gradle
    apply from: “relative/path/to/shared-dependencies.gradle"
    ...
    dependencies {
    ...
    // other dependencies
    }
    // build.gradle
    apply from: “relative/path/to/shared-dependencies.gradle"
    ...
    dependencies {
    ...
    // other dependencies
    }

    View Slide

  31. 1. Extract common dependencies
    2. Apply common dependencies to respective modules
    // build.gradle
    apply from: “relative/path/to/shared-dependencies.gradle"
    ...
    dependencies {
    ...
    // other dependencies
    }
    // build.gradle
    apply from: “relative/path/to/shared-dependencies.gradle"
    ...
    dependencies {
    ...
    // other dependencies
    }
    // build.gradle
    apply from: “relative/path/to/shared-dependencies.gradle"
    ...
    dependencies {
    ...
    // other dependencies
    }
    // build.gradle
    apply from: “relative/path/to/shared-dependencies.gradle"
    ...
    dependencies {
    ...
    // other dependencies
    }

    View Slide

  32. • Managing shared dependencies becomes easier
    • Eliminates repeated declaration of dependencies

    View Slide

  33. #2

    View Slide

  34. #2
    • Fairly complex projects
    • Multiple modules
    • You will find a lot of repetitions in the
    configurations of the modules

    View Slide

  35. #2

    View Slide

  36. #2
    // build.gradle
    android {
    compileSdkVersion sdk.compileSdkVersion
    buildToolsVersion sdk.buildToolsVersion
    defaultConfig {...}
    buildTypes {...}
    }

    View Slide

  37. #2
    // build.gradle
    android {
    compileSdkVersion sdk.compileSdkVersion
    buildToolsVersion sdk.buildToolsVersion
    defaultConfig {
    minSdkVersion sdk.minSdkVersion
    targetSdkVersion sdk.targetSdkVersion
    versionCode 198828
    versionName "1.5.0"
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    consumerProguardFiles 'consumer-rules.pro'
    ...
    }
    buildTypes {...}
    }

    View Slide

  38. #2
    // build.gradle
    android {
    compileSdkVersion sdk.compileSdkVersion
    buildToolsVersion sdk.buildToolsVersion
    defaultConfig {...}
    buildTypes {
    debug {
    minifyEnabled ...
    proguardFiles ...
    }
    release {
    minifyEnabled ...
    proguardFiles ...
    }
    }
    }

    View Slide

  39. #2
    // build.gradle
    android {
    compileSdkVersion sdk.compileSdkVersion
    buildToolsVersion sdk.buildToolsVersion
    defaultConfig {...}
    buildTypes {...}
    }

    View Slide

  40. #2
    // build.gradle
    android {
    compileSdkVersion sdk.compileSdkVersion
    buildToolsVersion sdk.buildToolsVersion
    defaultConfig {...}
    buildTypes {...}
    }
    // build.gradle
    android {
    compileSdkVersion sdk.compileSdkVersion
    buildToolsVersion sdk.buildToolsVersion
    defaultConfig {...}
    buildTypes {...}
    }

    View Slide

  41. #2
    // build.gradle
    android {
    compileSdkVersion sdk.compileSdkVersion
    buildToolsVersion sdk.buildToolsVersion
    defaultConfig {...}
    buildTypes {...}
    }
    // build.gradle
    android {
    compileSdkVersion sdk.compileSdkVersion
    buildToolsVersion sdk.buildToolsVersion
    defaultConfig {...}
    buildTypes {...}
    }
    // build.gradle
    android {
    compileSdkVersion sdk.compileSdkVersion
    buildToolsVersion sdk.buildToolsVersion
    defaultConfig {...}
    buildTypes {...}
    }

    View Slide

  42. #2
    // build.gradle
    android {
    compileSdkVersion sdk.compileSdkVersion
    buildToolsVersion sdk.buildToolsVersion
    defaultConfig {...}
    buildTypes {...}
    }
    // build.gradle
    android {
    compileSdkVersion sdk.compileSdkVersion
    buildToolsVersion sdk.buildToolsVersion
    defaultConfig {...}
    buildTypes {...}
    }
    // build.gradle
    android {
    compileSdkVersion sdk.compileSdkVersion
    buildToolsVersion sdk.buildToolsVersion
    defaultConfig {...}
    buildTypes {...}
    }
    Problem: Repeated configuration in so many modules

    View Slide

  43. 1. Extract common configuration into separate file
    https://handstandsam.com/2019/03/12/sharing-gradle-configuration-in-multi-module-android-projects/
    // common-android-config.gradle
    apply plugin: 'kotlin-android'
    android {
    compileSdkVersion sdk.compileSdkVersion
    defaultConfig {
    minSdkVersion sdk.minSdkVersion
    targetSdkVersion sdk.targetSdkVersion
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    }
    Further reading:

    View Slide

  44. 1. Extract common configuration into separate file
    https://handstandsam.com/2019/03/12/sharing-gradle-configuration-in-multi-module-android-projects/
    Further reading:
    2. Apply config to android modules
    // build.gradle
    apply from: "$rootProject.projectDir/common-android-config.gradle"
    ...

    View Slide

  45. • Eliminates repeated configuration of common build logic
    • Same applies to other common configuration you may have across
    multiple modules.
    https://handstandsam.com/2019/03/12/sharing-gradle-configuration-in-multi-module-android-projects/
    Further reading:

    View Slide

  46. Taking it even further with Kotlin
    DSL

    View Slide

  47. Why Kotlin DSL?
    • Kotlin is now really popular
    • Same language for code & build logic

    View Slide

  48. Why Kotlin DSL?
    • Kotlin is now really popular
    • Same language for code & build logic
    • Better IDE support for build scripts in Kotlin.
    • Jump to declaration, syntax highlighting, etc

    View Slide

  49. buildSrc
    • Gradle allows us create a module to add complex build related logic - like
    plugins, custom tasks, etc.

    View Slide

  50. buildSrc
    • Gradle allows us create a module to add complex build related logic - like
    plugins, custom tasks, etc.
    • It’s a regular module with it’s own:
    • build.gradle.kts
    • src/main/java or src/main/kotlin

    View Slide

  51. Adding dependencies versions to buildSrc
    Anatomy of the buildSrc module for dependencies versioning

    View Slide

  52. dependencies.gradle -> dependencies.kt
    // build.gradle.kts
    plugins {
    `kotlin-dsl`
    }
    repositories {
    jcenter()
    }

    View Slide

  53. dependencies.gradle -> dependencies.kt
    // Dependencies.kt
    object SdkVersions {
    const val compileSdkVersion = 28
    const val minSdkVersion = 21
    const val targetSdkVersion = 28
    }
    object Deps {
    const val kotlinStdLib = “org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41"
    const val gson = “com.google.code.gson:gson:2.8.5"
    ...
    object Rx {
    const val rxJava2 = "io.reactivex.rxjava2:rxjava:2.2.10"
    const val rxAndroid = "io.reactivex.rxjava2:rxandroid:2.1.1"
    }
    ...
    }

    View Slide

  54. dependencies.gradle -> dependencies.kt
    // Dependencies.kt
    object SdkVersions {
    const val compileSdkVersion = 28
    const val minSdkVersion = 21
    const val targetSdkVersion = 28
    }
    object Deps {
    const val kotlinStdLib = “org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41"
    const val gson = “com.google.code.gson:gson:2.8.5"
    ...
    object Rx {
    const val rxJava2 = "io.reactivex.rxjava2:rxjava:2.2.10"
    const val rxAndroid = "io.reactivex.rxjava2:rxandroid:2.1.1"
    }
    ...
    }

    View Slide

  55. dependencies.gradle -> dependencies.kt
    // Dependencies.kt
    object Deps {
    const val kotlinStdLib = “org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41"
    const val gson = “com.google.code.gson:gson:2.8.5"
    }
    // Usage in build.gradle
    dependencies {
    implementation Deps.kotlinStdLib
    implementation Deps.gson
    ...
    }

    View Slide

  56. dependencies.gradle -> dependencies.kt
    // Dependencies.kt
    object Deps {
    const val kotlinStdLib = “org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.41"
    const val gson = “com.google.code.gson:gson:2.8.5"
    }
    // Usage in build.gradle
    dependencies {
    implementation Deps.kotlinStdLib
    implementation Deps.gson
    ...
    }
    Syntax highlighting ✅
    “Jump to declaration” ✅

    View Slide

  57. Kotlin DSL prerequisites
    • Gradle 5.0
    • Java 8

    View Slide

  58. Tips for migrating to Kotlin DSL

    View Slide

  59. Tips for migrating to Kotlin DSL
    1. becomes
    2. Single quotes become double quotes
    3. Property assignments should have assignment operator ‘=‘
    4. Function calls should have parenthes

    View Slide

  60. Migrating an Android project

    View Slide

  61. // settings.gradle
    include ':app', ':data', ':domain'

    View Slide

  62. // settings.gradle
    include ':app', ':data', ':domain'
    // settings.gradle.kts
    include(":app", ":data", ":domain")

    View Slide

  63. // settings.gradle
    include ':app', ':data', ':domain'
    // settings.gradle.kts
    include(":app", ":data", ":domain")
    ’:app’ becomes “:app”
    include becomes include()

    View Slide

  64. // app-level build.gradle

    View Slide

  65. // app-level build.gradle
    apply plugin: 'com.android.application'
    apply plugin: 'kotlin-android'
    android {
    compileSdkVersion 28
    defaultConfig {
    applicationId "com.segunfamisa.kotlindsl"
    minSdkVersion 21
    targetSdkVersion 28
    versionCode 1
    versionName "1.0"
    }
    buildTypes {
    release {
    minifyEnabled true
    proguardFiles ...
    }
    }
    }
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
    implementation "androidx.appcompat:appcompat:1.0.2"
    }

    View Slide

  66. // app-level build.gradle.kts
    apply(plugin = "com.android.application")
    apply(plugin = "kotlin-android")
    android {
    compileSdkVersion(28)
    defaultConfig {
    applicationId "com.segunfamisa.kotlindsl"
    minSdkVersion(21)
    targetSdkVersion(28)
    versionCode = 1
    versionName = "1.0"
    }
    buildTypes {
    getByName("release") {
    isMinifyEnabled = true
    proguardFiles(getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro')
    }
    }
    }
    dependencies {
    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31")
    implementation("androidx.appcompat:appcompat:1.0.2")
    }

    View Slide

  67. What changed?
    apply plugin: 'com.android.application'
    apply plugin: 'kotlin-android'
    android {
    compileSdkVersion 28
    defaultConfig {
    applicationId "com.segunfamisa.kotlindsl"
    minSdkVersion 21
    targetSdkVersion 28
    versionCode 1
    versionName "1.0"
    }
    buildTypes {
    release {
    minifyEnabled true
    proguardFiles ...
    }
    }
    }
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
    implementation "androidx.appcompat:appcompat:1.0.2"
    }

    View Slide

  68. What changed?
    apply(plugin = "com.android.application")
    apply(plugin = "kotlin-android")
    android {
    compileSdkVersion 28
    defaultConfig {
    applicationId "com.segunfamisa.kotlindsl"
    minSdkVersion 21
    targetSdkVersion 28
    versionCode 1
    versionName "1.0"
    }
    buildTypes {
    release {
    minifyEnabled true
    proguardFiles ...
    }
    }
    }
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
    implementation "androidx.appcompat:appcompat:1.0.2"
    }

    View Slide

  69. What changed?
    apply(plugin = "com.android.application")
    apply(plugin = "kotlin-android")
    android {
    compileSdkVersion 28
    defaultConfig {
    applicationId "com.segunfamisa.kotlindsl"
    minSdkVersion 21
    targetSdkVersion 28
    versionCode 1
    versionName "1.0"
    }
    buildTypes {
    release {
    minifyEnabled true
    proguardFiles ...
    }
    }
    }
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
    implementation "androidx.appcompat:appcompat:1.0.2"
    }

    View Slide

  70. What changed?
    apply(plugin = "com.android.application")
    apply(plugin = "kotlin-android")
    android {
    compileSdkVersion(28)
    defaultConfig {
    applicationId "com.segunfamisa.kotlindsl"
    minSdkVersion(21)
    targetSdkVersion(28)
    versionCode 1
    versionName "1.0"
    }
    buildTypes {
    release {
    minifyEnabled true
    proguardFiles ...
    }
    }
    }
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
    implementation "androidx.appcompat:appcompat:1.0.2"
    }

    View Slide

  71. What changed?
    apply(plugin = "com.android.application")
    apply(plugin = "kotlin-android")
    android {
    compileSdkVersion(28)
    defaultConfig {
    applicationId "com.segunfamisa.kotlindsl"
    minSdkVersion(21)
    targetSdkVersion(28)
    versionCode 1
    versionName "1.0"
    }
    buildTypes {
    release {
    minifyEnabled true
    proguardFiles ...
    }
    }
    }
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
    implementation "androidx.appcompat:appcompat:1.0.2"
    }

    View Slide

  72. What changed?
    apply(plugin = "com.android.application")
    apply(plugin = "kotlin-android")
    android {
    compileSdkVersion(28)
    defaultConfig {
    applicationId "com.segunfamisa.kotlindsl"
    minSdkVersion(21)
    targetSdkVersion(28)
    versionCode 1
    versionName "1.0"
    }
    buildTypes {
    getByName("release") {
    isMinifyEnabled = true
    proguardFiles(getDefaultProguardFile(‘...’), 'proguard-rules.pro')
    }
    }
    }
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
    implementation "androidx.appcompat:appcompat:1.0.2"
    }

    View Slide

  73. What changed?
    apply(plugin = "com.android.application")
    apply(plugin = "kotlin-android")
    android {
    compileSdkVersion(28)
    defaultConfig {
    applicationId "com.segunfamisa.kotlindsl"
    minSdkVersion(21)
    targetSdkVersion(28)
    versionCode 1
    versionName "1.0"
    }
    buildTypes {
    getByName("release") {
    isMinifyEnabled = true
    proguardFiles(getDefaultProguardFile(‘...’), 'proguard-rules.pro')
    }
    }
    }
    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
    implementation "androidx.appcompat:appcompat:1.0.2"
    }

    View Slide

  74. What changed?
    apply(plugin = "com.android.application")
    apply(plugin = "kotlin-android")
    android {
    compileSdkVersion(28)
    defaultConfig {
    applicationId "com.segunfamisa.kotlindsl"
    minSdkVersion(21)
    targetSdkVersion(28)
    versionCode 1
    versionName "1.0"
    }
    buildTypes {
    getByName("release") {
    isMinifyEnabled = true
    proguardFiles(getDefaultProguardFile(‘...’), 'proguard-rules.pro')
    }
    }
    }
    dependencies {
    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31")
    implementation("androidx.appcompat:appcompat:1.0.2")
    }

    View Slide

  75. // app-level build.gradle.kts
    apply(plugin = "com.android.application")
    apply(plugin = "kotlin-android")
    android {
    compileSdkVersion(28)
    defaultConfig {
    applicationId "com.segunfamisa.kotlindsl"
    minSdkVersion(21)
    targetSdkVersion(28)
    versionCode = 1
    versionName = "1.0"
    }
    buildTypes {
    getByName("release") {
    isMinifyEnabled = true
    proguardFiles(getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro')
    }
    }
    }
    dependencies {
    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31")
    implementation("androidx.appcompat:appcompat:1.0.2")
    }

    View Slide

  76. // top-level build.gradle

    View Slide

  77. // top-level build.gradle
    buildscript {
    ext.kotlin_version = '1.3.31'
    repositories {
    google()
    jcenter()
    }
    dependencies {
    classpath 'com.android.tools.build:gradle:3.4.1'
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
    }
    allprojects {
    repositories {
    google()
    jcenter()
    }
    }

    View Slide

  78. // top-level build.gradle.kts
    buildscript {
    extra.apply {
    set("kotlin_version", "1.3.31")
    }
    repositories {
    google()
    jcenter()
    }
    dependencies {
    classpath("com.android.tools.build:gradle:3.4.1")
    classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${extras.get("kotlin_version")})
    }
    }
    allprojects {
    repositories {
    google()
    jcenter()
    }
    }

    View Slide

  79. What changed?

    View Slide

  80. // top-level build.gradle
    buildscript {
    ext.kotlin_version = '1.3.31'
    repositories {
    google()
    jcenter()
    }
    dependencies {
    classpath 'com.android.tools.build:gradle:3.4.1'
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
    }
    allprojects {
    repositories {
    google()
    jcenter()
    }
    }
    What changed?

    View Slide

  81. What changed?
    // top-level build.gradle.kts
    buildscript {
    extra.apply {
    set("kotlin_version", "1.3.31")
    }
    repositories {
    google()
    jcenter()
    }
    dependencies {
    classpath("com.android.tools.build:gradle:3.4.1")
    classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${extras.get("kotlin_version")})
    }
    }
    allprojects {
    repositories {
    google()
    jcenter()
    }
    }

    View Slide

  82. // top-level build.gradle
    buildscript {
    extra.apply {
    set("kotlin_version", "1.3.31")
    }
    repositories {
    google()
    jcenter()
    }
    dependencies {
    classpath 'com.android.tools.build:gradle:3.4.1'
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
    }
    allprojects {
    repositories {
    google()
    jcenter()
    }
    }
    What changed?

    View Slide

  83. // top-level build.gradle.kts
    buildscript {
    extra.apply {
    set("kotlin_version", "1.3.31")
    }
    repositories {
    google()
    jcenter()
    }
    dependencies {
    classpath("com.android.tools.build:gradle:3.4.1")
    classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${extras.get("kotlin_version")})
    }
    }
    allprojects {
    repositories {
    google()
    jcenter()
    }
    }
    What changed?

    View Slide

  84. // top-level build.gradle.kts
    buildscript {
    extra.apply {
    set("kotlin_version", "1.3.31")
    }
    repositories {
    google()
    jcenter()
    }
    dependencies {
    classpath("com.android.tools.build:gradle:3.4.1")
    classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${extras.get("kotlin_version")})
    }
    }
    allprojects {
    repositories {
    google()
    jcenter()
    }
    }

    View Slide

  85. Custom Gradle Plugin

    View Slide

  86. Custom Gradle Plugin
    • We could use a custom plugin to apply common configuration
    across multiple modules

    View Slide

  87. class AwesomePlugin : Plugin {
    override fun apply(project: Project) {
    ...
    }
    }

    View Slide

  88. class AwesomePlugin : Plugin {
    override fun apply(project: Project) {
    with(project) {
    project.plugins.all {
    when (this) {
    is AppPlugin,
    is LibraryPlugin -> extensions.getByType().apply {
    configureAndroidModule(extension = this, project = project)
    }
    }
    }
    }
    }
    }

    View Slide

  89. class AwesomePlugin : Plugin {
    override fun apply(project: Project) {
    with(project) {
    project.plugins.all {
    when (this) {
    is AppPlugin,
    is LibraryPlugin -> extensions.getByType().apply {
    configureAndroidModule(extension = this, project = project)
    }
    }
    }
    }
    }
    }

    View Slide

  90. class AwesomePlugin : Plugin {
    override fun apply(project: Project) {
    with(project) {
    project.plugins.all {
    when (this) {
    is AppPlugin,
    is LibraryPlugin -> extensions.getByType().apply {
    configureAndroidModule(extension = this, project = project)
    }
    }
    }
    }
    }
    }

    View Slide

  91. private fun TestedExtension.configureAndroidModule(project: Project) {
    }

    View Slide

  92. private fun TestedExtension.configureAndroidModule(project: Project) {
    setCompileSdkVersion(28)
    defaultConfig.apply {
    minSdkVersion(21)
    targetSdkVersion(28)
    compileOptions {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
    }
    }
    ...
    }

    View Slide

  93. Apply new plugin to all modules
    plugins {
    id("com.android.application")
    kotlin("android")
    kotlin("android.extensions")
    }
    apply {
    from("${rootProject.projectDir}/shared-dependencies.gradle")
    }
    apply()
    ...

    View Slide

  94. Current pain points with Kotlin DSL

    View Slide

  95. Current pain points with Kotlin DSL
    • IDE support is not yet at

    View Slide

  96. Current pain points with Kotlin DSL
    • IDE support is not yet at
    • Additional configuration time in your build process

    View Slide

  97. Current pain points with Kotlin DSL
    • IDE support is not yet at
    • Additional configuration time in your build process
    • Applying config from an external gradle.kts file doesn’t quite work:

    • You either have to leave as .gradle files or follow another work-around
    (see: https://github.com/gradle/kotlin-dsl/issues/1287)

    View Slide

  98. Current pain points with Kotlin DSL
    • IDE support is not yet at
    • Additional configuration time in your build process
    • Applying config from an external gradle.kts file doesn’t quite work:

    • You either have to leave as .gradle files or follow another work-around
    (see: https://github.com/gradle/kotlin-dsl/issues/1287)

    • Weird, but sometimes, some things in the plugin show a compiler warning,
    but it actually works

    View Slide

  99. Final words

    View Slide

  100. Final words
    • Keep it DRY (Don’t Repeat Yourself) - rule of three applies here

    View Slide

  101. Final words
    • Keep it DRY (Don’t Repeat Yourself) - rule of three applies here
    • Modular code rocks

    View Slide

  102. Final words
    • Keep it DRY (Don’t Repeat Yourself) - rule of three applies here
    • Modular code rocks
    • Take advantage of IDE support with Kotlin DSL

    View Slide

  103. Resources
    https://handstandsam.com/2019/03/12/sharing-gradle-configuration-in-multi-module-android-projects/
    https://segunfamisa.com/posts/android-gradle-extra-properties
    https://gradle.com/blog/getting-started-with-gradle-kotlin-dsl/
    https://github.com/gradle/kotlin-dsl
    Build Bigger, Better: Gradle for Large Projects (Google I/O'19)

    View Slide

  104. Thanks!
    Segun Famisa, Android GDE

    View Slide