Android Gradle スクリプトを Groovy から Kotlin DSL へ移行する実践ガイド

Gradle 7.3.3 / AGP 7.2.1 を対象に、プロジェクト全体を Groovy DSL から Kotlin DSL へ移行した際のポイントをまとめます。Groovy の「メソッドとプロパティの区別が曖昧」「文字列リテラルが勝手にメソッド呼び出しになる」といった挙動は Kotlin DSL では型安全性の高い記述に置き換わります。特に拡張プロパティ(extra)と Maven 公開タスクの書き換えが大きな山場です。

1. ファイル名の変更

*.gradle*.gradle.kts にリネームするだけで Android Studio が Kotlin Script として認識します。

2. ルートレベル build.gradle.kts

apply(from = "config.gradle.kts")
apply(plugin = "com.alibaba.arouter")

buildscript {
    repositories {
        maven("https://maven.aliyun.com/repository/public")
    }
    dependencies {
        classpath(libs.android.gradlePlugin)
        classpath(libs.arouter.register)
    }
}

allprojects {
    repositories {
        maven("https://maven.aliyun.com/repository/public")
    }
    configurations.configureEach {
        resolutionStrategy.force("androidx.appcompat:appcompat:1.4.2")
        resolutionStrategy.force("androidx.recyclerview:recyclerview:1.2.1")
    }
}

tasks.register<Delete>("clean") {
    delete(rootProject.buildDir)
}

3. モジュールレベル build.gradle.kts

plugins {
    id("com.android.application")
    id("org.greenrobot.greendao")
}

android {
    compileSdk = libs.versions.compileSdk.get().toInt()
    ndkVersion = libs.versions.ndkVersion.get()

    defaultConfig {
        applicationId = "com.myapp.sample"
        minSdk = libs.versions.minSdk.get().toInt()
        targetSdk = libs.versions.targetSdk.get().toInt()
        versionCode = 1
        versionName = "1.0"
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        multiDexEnabled = true
        externalNativeBuild.cmake {
            cppFlags += ""
            abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86")
        }
    }

    signingConfigs {
        create("release") {
            keyAlias = "appkey"
            keyPassword = "******"
            storeFile = file("key.jks")
            storePassword = "******"
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = true
            isZipAlignEnabled = true
            signingConfig = signingConfigs["release"]
            proguardFiles(
                getDefaultProguardFile("proguard-android.txt"),
                "proguard-rules.pro",
                "proguard-android.pro",
                "proguard-third-libs.pro"
            )
        }
        debug {
            isMinifyEnabled = false
            isDebuggable = true
            signingConfig = signingConfigs["release"]
        }
    }

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }

    buildFeatures.dataBinding = true

    greendao.schemaVersion = 1

    externalNativeBuild.cmake {
        path("src/main/cpp/CMakeLists.txt")
        version = "3.10.2"
    }

    lintOptions.isAbortOnError = false
}

dependencies {
    configurations.configureEach {
        resolutionStrategy.cacheChangingModulesFor(30, TimeUnit.SECONDS)
    }
    implementation(libs.glide)
    annotationProcessor(libs.glide.compiler)
    implementation(project(":easyPhotos"))
    implementation(libs.androidx.constraintlayout)
    implementation(libs.google.zxing.core) {
        exclude(mapOf("group" to "com.google.guava"))
    }
}

4. 拡張プロパティの扱い

4.1 値の追加

// パターン1:Map 風
rootProject.extra.apply {
    set("userName", "AryaStark")
    set("passPhrase", "valarMorghulis")
}

// パターン2:委譲プロパティ
val userName: String by rootProject.extra("AryaStark")
val passPhrase: String by rootProject.extra("valarMorghulis")

// パターン3:ブロックで計算
val userName: String by rootProject.extra {
    val tmp = "AryaStark"
    tmp
}

4.2 値の取得

// Map 風
val user = rootProject.extra["userName"] as String
val pwd  = rootProject.extra["passPhrase"] as String

// 委譲プロパティ(変数名が extra のキーと一致する必要あり)
val userName: String by rootProject.extra
val passPhrase: String by rootProject.extra

5. Maven 公開タスク

5.1 ライブラリモジュール内に記述

plugins {
    id("com.android.library")
    `maven-publish`
}

android {
    publishing {
        singleVariant("release") { withSourcesJar() }
    }
}

afterEvaluate {
    val GROUP_ID   = "com.example.sdk"
    val ARTIFACT   = "core-util"
    val VERSION    = "1.0.0"

    publishing {
        publications {
            register<MavenPublication>("release") {
                from(components["release"])
                groupId    = GROUP_ID
                artifactId = ARTIFACT
                version    = VERSION
                pom.withXml {
                    println("Upload: $groupId:$artifactId:$version")
                    configurations["implementation"].allDependencies.forEach {
                        if (it.version != "unspecified") {
                            println("   ${it.group}:${it.name}:${it.version}")
                        }
                    }
                }
            }
        }
        repositories {
            maven {
                url = uri(rootProject.extra["mavenUrl"] as String)
                credentials {
                    username = rootProject.extra["userName"] as String
                    password = rootProject.extra["passPhrase"] as String
                }
            }
        }
    }
}

5.2 共通スクリプトとして切り出す

build.gradle.kts

plugins { id("com.android.library") }
apply(from = "${rootProject.rootDir}/publish.maven.gradle.kts")

publish.maven.gradle.kts

import org.gradle.api.publish.PublishingExtension

apply(plugin = "maven-publish")

val catalog: Map<String, Map<String, String>> by rootProject.extra
val lib = catalog[project.name]!!
val sona: Map<String, String> by rootProject.extra

val repoUrl = if (lib["version"]!!.endsWith("SNAPSHOT")) {
    sona["snapshotUrl"]!!
} else {
    sona["releaseUrl"]!!
}

configure<PublishingExtension> {
    publications {
        register<MavenPublication>("aar") {
            afterEvaluate { from(components["release"]) }
            groupId    = rootProject.extra["groupId"] as String
            artifactId = lib["artifactId"]
            version    = lib["version"]
            pom.withXml {
                println("${project.name} → $groupId:$artifactId:$version")
                configurations["implementation"].allDependencies.forEach {
                    if (it.version != "unspecified") {
                        println("   ${it.group}:${it.name}:${it.version}")
                    }
                }
            }
        }
    }
    repositories {
        maven {
            url = uri(repoUrl)
            credentials {
                username = sona["userName"]
                password = sona["passPhrase"]
            }
        }
    }
}

タグ: gradle-kotlin-dsl android-gradle-plugin maven-publish build-gradle-migration extra-properties

5月18日 08:15 投稿