はじめに
タイトルにあるように build.gradle.ktsをAndroidアプリ開発で使っていこうと思います.
また,マルチモジュールでプロジェクトを運用する際に同じような記述を共通化させる部分の紹介もしたいと思います.
最初,Android StudioでNew Projectすると build.gradleというGroovyで記述されたスクリプトファイルが生成されます.
Groovyはあまり聞き慣れませんが,Javaから派生した動的型付けのプログラミング言語です.
build.gradleでGroovyを使う際のイマイチな点として以下のものが挙げられると思います.
- 普段はJavaやKollinで開発するのに build.gradleだけGroovyで書かなければいけないく,JavaやKotlinで得た知見をbuild.gradleに活かせない
- 補完が効かない
大きくこの2つかなと個人的な見解として思っています.
DroidKaigi2019のセッションでも紹介されていました.
これを解決するためにbuild.gradleをKotlinで記述してAndroidアプリ開発をしようというときに出てくるのがbuild.gradle.ktsです.今までGroovyで記述していたbuild.gradleをKotlinで記述することができるようになります.
ではやっていきましょう.
Re:ゼロから始めるbuild.gradle.kts生活
やっていく
Step1
まずは,プロジェクト直下にbuildSrcという名前でdirectoryを作ります.
その中に build.gradle.ktsファイル を作って以下の中身を書きます.
plugins {
    `kotlin-dsl`
}
repositories {
    jcenter()
}
書いたらsync nowします
色々ファイルが生成されます
Step2
次に buildSrc/src/main/java/dependenciesとなるようにpackageを作ります
dependenciesの中に Libs.kt作ります.
この中にはプロジェクトで必要なライブラリの依存関係を記述します.
よく Dep.ktという名前で作りますが,今回は後々 Dependencies.ktというファイルを作るときに名前が被るので適当に Libs.ktという名前にしておきたいと思います.
Libs.ktの中身はこのように書いていきます.ここはDroidKaigi/conference-app-2020のDep.ktが参考になると思います.
object Dep {
    object Kotlin {
        val stdlib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61"
    }
}
Step3
buildSrcの中のbuild.gradle.ktsに追記
plugins {
    `kotlin-dsl`
}
repositories {
    jcenter()
    google()  // 追記
}
dependencies {
    implementation("com.android.tools.build:gradle:4.0.1")  // 追記
}
com.android.tools.build:gradleのバージョンは適宜変えてください.
これを入れることでこのあとマルチモジュールした際の依存関係の共通化をするコードを書くことができるようになります.
Step4
次にもともとbuild.gradleにあった androidブロックを共通化させるためのコードを書いていきます,
マルチモジュールでプロジェクトを作るとき,デフォルトではこの部分はすべてのモジュールで同じことを書かなければいけません.
そこで,buildSrc/src/main/java/dependenciesの中に BuildConfig.ktというような名前でファイルを作成します.
中身は以下のように記述します.
package dependencies
import com.android.build.gradle.BaseExtension
object BuildConfig {
    const val applicationId = "com.example.app"
    const val versionCode = 1
    const val versionName = "1.0"
    const val androidTargetSdkVersion = 29
    const val androidCompileSdkVersion = 29
    const val androidMinSdkVersion = 23
    const val buildToolsVersion = "29.0.3"
    const val testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    const val consumerProguardFiles = "consumer-rules.pro"
}
fun BaseExtension.baseExtension() {
    compileSdkVersion(BuildConfig.androidCompileSdkVersion)
    buildToolsVersion(BuildConfig.buildToolsVersion)
    defaultConfig {
        minSdkVersion(BuildConfig.androidMinSdkVersion)
        targetSdkVersion(BuildConfig.androidTargetSdkVersion)
        versionCode = BuildConfig.versionCode
        versionName = BuildConfig.versionName
        testInstrumentationRunner = BuildConfig.testInstrumentationRunner
        consumerProguardFiles(BuildConfig.consumerProguardFiles)
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false
            proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
        }
    }
} 
Step5
実際に使ってみます
appにある build.gradleを build.gradle.ktsにrenameします.
中身は以下のように書き換えます.
一番下の dependenciesのブロックは今はおいておきます.
plugins {
    id("com.android.application")
    id("kotlin-android")
    id("kotlin-android-extensions")
}
android {
    baseExtension()
    defaultConfig.applicationId = BuildConfig.applicationId
}
先程の BuildConfig.ktで baseExtensionという名前で定義したものが使えています.
このように実際に使う際は androidブロック内で baseExtension()と一行書くだけで冗長だったコードを共通化することができています.
Step6
では次にdependenciesブロックについてです.いつも使いたいライブラリの依存関係を定義するところですね.
ここも例えば features/homeと features/settingsみたいなモジュールを作った際に使うライブラリの依存関係が同じようになることがよくあります.
その部分もある程度のベースとなる依存関係は共通化させるようにします.
buildSrc/src/main/java/dependenciesに Dependencies.ktを作ります.
中身は以下のように書いてみます.
fun Project.baseDependencies(additionalConfiguration: DependencyHandlerScope.() -> Unit) {
    dependencies {
        implementation(Libs.Kotlin.stdlib)
        implementation(Libs.AndroidX.appCompat)
        implementation(Libs.AndroidX.coreKtx)
        implementation(Libs.AndroidX.constraint)
    }
    dependencies(additionalConfiguration)
}
private fun DependencyHandler.implementation(depName: Any) {
    add("implementation", depName)
}
Step7
では,先程作った baseDependenciesを使ってみようと思います.
appの build.gradle.ktsに dependenciesを追加していきます.
中身を以下のようにします.
import dependencies.baseExtension
import dependencies.BuildConfig
plugins {
    id("com.android.application")
    id("kotlin-android")
    id("kotlin-android-extensions")
}
android {
    baseExtension()
    defaultConfig.applicationId = BuildConfig.applicationId
}
baseDependencies {
}
いままで dependenciesで書いて中に implementationを書いていましたが,どこでも使うようなベースになる依存関係はすべて baseDependenciesだけでかけるようになりました.
もちろん baseDependenciesの中でそのモジュール内でまた別に定義したい依存関係は implementationで適宜追加できます.
おまけ
1つのライブラリを使いたいときにそれに関連する依存関係を複数まとめて書きたい場合があると思います.
そのときに, Dependencies.ktに以下のようなコードを追加してみます.
fun DependencyHandler.groupie() {
    implementation(Libs.Groupie.groupie)
    implementation(Libs.Groupie.databinding)
}
そうすると,build.gradle.ktsで今回だとGroupieで使いたい複数の依存関係を以下のように一行ですっきり書くことができるようになります.
import dependencies.baseExtension
import dependencies.BuildConfig
plugins {
    id("com.android.application")
    id("kotlin-android")
    id("kotlin-android-extensions")
}
android {
    baseExtension()
    defaultConfig.applicationId = BuildConfig.applicationId
}
baseDependencies {
    groupie()
}
さいごに
こんな感じで今まで冗長だった依存関係の記述をすっきり,共通化させてしかもKotlinで書くことができるようになります.
みなさんも良き build.gradle.kts生活を.