Help us understand the problem. What is going on with this article?

Gradle を Kotlin で書こう

今までなんとなくコピペとかコピペとかコピペで書いていた gradle 。
Groovy とか言う音ゲーのタイトルっぽいスクリプトで書かれています。

なんとその gradle が Kotlin で記述できるではありませんか!
これで少しは理解に身が入ると言うもの。さっそく試したいと思いました。

Kotlin DSL ??

  • Domain-Specific Language

特定の課題を解決するために特化された言語
SQL や HTML, Groovy なんかも DSL になるらしいですね。

Kotlin でスクリプトが書ける機能です。

Migration

今回は元々あるプロジェクトへの導入方法になります。

Environment

  • Android Studio 3.5.3
  • Gradle 6.0.1
  • Kotlin 1.3.61
  • JDK 8

Rename and Rewrite

ほぼほぼ Android Studio の New Project で自動生成される gradle を書き換えます。

1. settings.gradle -> settings.gradle.kts に Rename and Rewrite

settings.gradle.kts
include(":app")
rootProject.name = "MyApplication"

2. ルートレベルの build.gradle -> build.gradle.kts に Rename and Rewrite

build.gradle.kts
buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath("com.android.tools.build:gradle:3.5.3")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61")
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

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

3. モジュール内の build.gradle -> build.gradle.kts に Rename and Rewrite

app/build.gradle.kts
plugins {
    id("com.android.application")

    kotlin("android")

    kotlin("android.extensions")
}

android {
    compileSdkVersion(29)
    buildToolsVersion = "29.0.0"
    defaultConfig {
        applicationId = "<ApplicationId>"
        minSdkVersion(23)
        targetSdkVersion(29)
        versionCode = 1
        versionName = "0.1.0"
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false
            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.61")
    implementation("androidx.appcompat:appcompat:1.1.0")
    implementation("androidx.core:core-ktx:1.2.0-rc01")
    implementation("androidx.constraintlayout:constraintlayout:2.0.0-beta2")
    testImplementation("junit:junit:4.12")
    androidTestImplementation("androidx.test.ext:junit:1.1.1")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.2.0")
}

4. Sync Now !!!😎

Success!!😎

依存関係をまとめる

これで Kotliner として一歩前に進んだ気がします。やったね。

意気揚々と Android 開発のバイブル DroidKaigi のリポジトリを見に行くと何やら見た事ない書き方が。

conference-app-2020/build.gradle at master · DroidKaigi/conference-app-2020 · GitHub

import dependencies.Dep

...

dependencies {
    ...

    implementation Dep.Kotlin.stdlibJvm
    implementation Dep.AndroidX.appCompat
    implementation Dep.AndroidX.coreKtx
    implementation Dep.AndroidX.constraint
    implementation Dep.AndroidX.activityKtx

    ...
}

Packages?? Dep?? 🤔

依存関係を定義して使い回す

マルチモジュールで開発する時にライブラリの情報とかがいろんなところに散りばめられて管理が大変らしい。
それらを buildSrc に纏めると一元管理できていいよね😂 との事。

たしかに見やすくなっているので実際に試してみました。

1. プロジェクトのルートに buildSrc ディレクトリを作成

まず object を定義するモジュール? 用のディレクトリを作成します。

  • 普通にディレクトリを作成します 。 ( Android Studio では New -> Directory -> buildSrc )

Add Module とかではない。(最初間違えました。。。)

2. Sync Now !!!😎

.gradle, build ができあがると思います。

3. build.gradle.kts を作成

build.gradle.kts
plugins {
    `kotlin-dsl`
}
repositories {
    jcenter()
}

4. 変数の定義をするファイルを作成

今回は src/main/kotlin/dependencies 配下に置きました。
これも普通に src -> main -> kotlin -> ... とディレクトリを作成していきます。

あとはソースファイルを作成していつもの様に変数定義します。

我らが DroidKaigi さんは Package 情報も切り出していたので真似してみました。

dependencies/Dep.kt
object Dep {
    object Plugin {
        val android = "com.android.tools.build:gradle:3.5.3"
        val kotlin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Kotlin.version}"
        val safeArgs = "androidx.navigation:navigation-safe-args-gradle-plugin:${AndroidX.Navigation.version}"
    }

    object Test {
        val junit = "junit:junit:4.12"
        val androidJunit = "androidx.test.ext:junit:1.1.1"
        val espressoCore = "androidx.test.espresso:espresso-core:3.2.0"
    }

    object Kotlin {
        const val version = "1.3.61"
        val stdlibJdk = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$version"
    }

    object AndroidX {
        val appCompat = "androidx.appcompat:appcompat:1.1.0"
        val coreKtx = "androidx.core:core-ktx:1.2.0-rc01"
        val constraint = "androidx.constraintlayout:constraintlayout:2.0.0-beta2"
    }
}
dependencies/Packages.kt
object Packages {
    const val id = "<Application Id>"
    const val debugIdSuffix = ".debug"

    object SdkVersion {
        const val target = 29
        const val compile = 29
        const val min = 21
    }

    object Version {
        private const val major = 0
        private const val minor = 1
        private const val build = 0

        val code = (major * 100 + minor * 10 + build)
        val name = "$major.$minor.$build"
    }
}

5. Sync Now !!!😎

準備できました。
あとは gradle ファイル内で import して使うだけ。

6. import

import dependencies.Packages
import dependencies.Dep

android {
    compileSdkVersion(Packages.SdkVersion.compile)
    buildToolsVersion = Packages.SdkVersion.compile.toString()
    defaultConfig {
        applicationId = Packages.id
        minSdkVersion(Packages.SdkVersion.min)
        targetSdkVersion(Packages.SdkVersion.target)
        versionCode = Packages.Version.code
        versionName = Packages.Version.name
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false
            proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
        }
    }
}

dependencies {
    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
    implementation(Dep.Kotlin.stdlibJdk)
    implementation(Dep.AndroidX.appCompat)
    implementation(Dep.AndroidX.coreKtx)
    implementation(Dep.AndroidX.constraint)

    testImplementation(Dep.Test.junit)
    androidTestImplementation(Dep.Test.androidJunit)
    androidTestImplementation(Dep.Test.espressoCore)
}

この調子でルートレベルの gradle も書き換えます

build.gradle.kts
import dependencies.Dep

buildscript {
    dependencies {
        classpath(Dep.Plugin.android)
        classpath(Dep.Plugin.kotlin)
    }
}

7. Sync now !!!😎

Succe...ん??

c7b7ac52.png

org.gradle.internal.exceptions.LocationAwareException: Build file '../build.gradle.kts' line: 10
Script compilation errors:

  Line 10:         classpath(Dep.Plugin.android)
                             ^ Unresolved reference: Dep

  Line 11:         classpath(Dep.Plugin.kotlin)
                             ^ Unresolved reference: Dep

あれ?参照できない??
補完はちゃんと効いてるしコンパイルした時だけエラーになる。。。
buildSrc の build が走ったあとの Configuration でエラーだし。。。
Android Studio を 3.6 にあげてもだめ。。。

試しに src/main/kotlin 配下に置くか build.gradle を Groovy で書くと通りました。なぜだ。。。

ひとまず Plugin 関連の定義だけ別ファイルに切り出して直下に置く対応をしました。

Conclusion

マルチモジュールではない場合の恩恵はそこまでなのかもしれませんが、
補完が効くので普通に書きやすいでした。
あとエラーの箇所もわかりやすいのでよき。

新しい事を学習するのは楽しいですね。(遅)
しかし、今年の DroidKaigi リポジトリでは Groovy で書かれていましたがどうしてなんでしょう。( Plugin の参照エラーのため?? )
この記事を投稿するほんの少し前に Issue あげられてました!!
名乗りあげようと思ったらもう Assigne 決まってて残念です…

今回やったことの他、Jetpack のライブラリ等、ナウなインプルをスタディするために
LINE の既読無視をするためのアプリを仕事の合間に開発中です。

GitHub - tick-taku/NotificationWatcher

次回は Room の勉強か coroutine の勉強か。
たぶん Room かな。。。

参考

【Android】dependenciesをIDE補完で記述する - Qiita

The New Way of Writing Build Gradle with Kotlin DSL

【Android】buildSrcを使ってライブラリの定義をまとめる | ITcowork Staff Blog

GitHub - DroidKaigi/conference-app-2020: The Official Conference App for DroidKaigi 2020 Tokyo

tick-taku
Android えんじにゃー。猫と星とロードバイクが好きです。 #Android #Kotlin #AWS #Python
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away