batchのブログ

知見の備忘録

Re:ゼロから始めるbuild.gradle.kts生活

はじめに

タイトルにあるように build.gradle.ktsAndroidアプリ開発で使っていこうと思います. また,マルチモジュールでプロジェクトを運用する際に同じような記述を共通化させる部分の紹介もしたいと思います.

最初,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.gradlebuild.gradle.ktsにrenameします. 中身は以下のように書き換えます. 一番下の dependenciesのブロックは今はおいておきます.

plugins {
    id("com.android.application")
    id("kotlin-android")
    id("kotlin-android-extensions")
}
android {
    baseExtension()
    defaultConfig.applicationId = BuildConfig.applicationId
}

先程の BuildConfig.ktbaseExtensionという名前で定義したものが使えています. このように実際に使う際は androidブロック内で baseExtension()と一行書くだけで冗長だったコードを共通化することができています.

Step6

では次にdependenciesブロックについてです.いつも使いたいライブラリの依存関係を定義するところですね. ここも例えば features/homefeatures/settingsみたいなモジュールを作った際に使うライブラリの依存関係が同じようになることがよくあります. その部分もある程度のベースとなる依存関係は共通化させるようにします. buildSrc/src/main/java/dependenciesDependencies.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.ktsdependenciesを追加していきます. 中身を以下のようにします.

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生活を.

参考記事