💡はじめに
こんにちは。ニフティ株式会社のLinです。
台湾出身のモバイルアプリエンジニアとして、社内で「マイ ニフティ」のAndroidおよびiOS版の開発を担当しております。
今回は、マイニフティ Android 2.0.4 で導入した、Gradle の Groovy DSL → Kotlin DSL → Version Catalog への移行をご紹介します。
🐘 Gradleの進化
Gradleは、Androidアプリの構築とビルドを管理するビルドシステムで、下記の機能があります:
- プロジェクトの構成管理
- ライブラリの依存関係管理
- リソースファイルの管理
- プロダクションビルド、デバッグビルドの切り替え
- APKファイルのビルド
マスコットはGradle君という名前の可愛い象です。
![](https://engineering.nifty.co.jp/wp-content/uploads/2024/05/Gradle_logo.png)
Android 開発における Gradle Script の記述方法には、Groovy DSL と Kotlin DSL の2つのパターンがあります。
Groovy DSL は従来からよく使われている記法で、シングルクォーテーションと括弧なしのダブルクォーテーションが特徴的です。
1 |
implementation 'androidx.room:room-rxjava2:$room_version' |
1 |
implementation "androidx.datastore:datastore-preferences:1.0.0" |
そして、2020年4月の Android Gradle Plugin 4.0.0 から、Kotlin DSL が導入されました。
括弧付きのダブルクォーテーションが特徴で、静的型付けによる型安全などのメリットがあります。
1 |
implementation("androidx.room:room-rxjava2:$room_version") |
しかし、Android の発展に伴い、Gradle の既存機能にはいくつかの課題がありました:
- 異なる記法がbuild.gradleファイルに混在しており、保守運用がやや複雑になってしまっている
- マルチモジュールの場合、複数のbuild.gradleに重複するコードを記述しなければならない
- バージョンアップする際に、変更箇所が散在してしまう
どうしよう…
Gradle君の悩みはどんどん大きくなった。
![](https://engineering.nifty.co.jp/wp-content/uploads/2024/05/20ba148259dbb9e6afaa5354794715f1.webp)
🧩 課題解決
課題解決のため、Gradle君はライブラリバージョン管理の改善を試し、二つ改善方法を見つけました:
- BuildSrc
buildSrc/build.gradle
に共通の部分を集約することができ、その中身は以前の build.gradle と同様の書き方です- どのGradleバージョンにも使えます
- ただし、下記理由のため、一時対策として使われています
- 機能ごとの複数の依存関係をまとめるのは手間がかかります
- pluginsの集約はできません
- モジュール間のコンフリクトを完全に避けるのは難しい
- 拡張性に制限があります
- Version Catalog
libs.versions.toml
に共通のバージョン、依存関係、プラグインを集約することができ、その中身は以前の build.gradle とは異なります- Gradle 7.4以上で導入された機能のため、バージョン制限があります
- Gradle 7.0 – 7.4 では Feature Preview、Gradle 7.0 以下では使えません
- 下記理由のため、恒久対策として使われています
- 機能ごとの複数の依存関係をまとめることができます
[bundles]
- pluginsの集約もできます
[plugins]
- Kotlin DSLのみで記述できるため、型安全などの利点があります
- Android Studioの新規プロジェクトでもVersion Catalogがデフォルトになったため、これからも業界の標準となっていくでしょう
- 機能ごとの複数の依存関係をまとめることができます
では、Gradle君と一緒にVersion Catalogを使ってみよう!
💻 Groovy DSL → Kotlin DSL → Version Catalog
移行作業には以下の2つのステップがあります:
- Kotlin DSLへの移行
- 下記ファイルの書き換え
→settings.gradlesettings.gradle.kts
- Groovy DSL → Kotlin DSL
include ':app'
↓include(":app")
- pluginManagementの追加
- @Incubatingの警告が出る場合は、
@file:Suppress("UnstableApiUsage")
も追加してください
- Groovy DSL → Kotlin DSL
→build.gradle(Project層)build.gradle.kts(Project層)
- Groovy DSL → Kotlin DSL
kotlin_version = '1.9.23'
↓val kotlinVersion: String by extra("1.9.23")
- Groovy DSL → Kotlin DSL
→build.gradle(Module層)build.gradle.kts(Module層)
- Groovy DSL → Kotlin DSL
- buildConfigの移行(AGP 9.0で廃止予定)
- 下記ファイルの書き換え
- Version Catalogへの移行
[versions]
:すべてのライブラリのバージョンを集約します- 定義した変数は
version.ref
で使用されます
- 定義した変数は
[libraries]
:すべてのライブラリのパスを集約します- module (group:name) より group + name の方がおすすめです
library = { module = "... : ...", version.ref = "..." }
↓library = { group = "...", name = "...", version.ref = "..." }
- BOMの集約もできます
- module (group:name) より group + name の方がおすすめです
[bundles]
:機能ごとの複数のライブラリをまとめます[libraries]
で定義したライブラリの変数名を、[bundles]
ごとにまとめます
[plugins]
:pluginsのclasspathとidを集約します[plugins]
で定義したものは、build.gradleのpluginsブロックでalias(...)
で呼び出せます- 例:Compose Compilerの移行
– libs.versions.toml
– build.gradle.kts(Project層)
– build.gradle.kts(Module層)
[bundles]
を活用すれば、必要な依存関係を1回のimplementationで一括して指定できます。
1 2 3 4 5 6 7 8 9 10 11 12 |
// libs.versions.toml [versions] room = "2.6.1" [libraries] androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } [bundles] room = ["androidx-room-runtime", "androidx-room-ktx"] |
1 2 3 4 5 6 7 8 9 |
// build.gradle.kts(Module層) ... dependencies { ... // Room implementation(libs.bundles.room) ksp(libs.androidx.room.compiler) } |
そして、移行作業が完了したら、Gradle Sync
を忘れずに行いましょう。
💫 成果
これまでは各 Gradle ファイルに散在していたライブラリバージョン情報は、下記のようにVersion Catalogに集約しました:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// libs.versions.toml [versions] activityCompose = "1.9.0" agp = "8.4.0" appcompat = "1.6.1" ... [libraries] androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } ... [bundles] android-test = ["androidx-junit", "androidx-espresso-core"] androidx = ["androidx-core-ktx", "androidx-appcompat", "material", "androidx-constraintlayout-compose", "androidx-activity-compose", "androidx-lifecycle-runtime-ktx", "androidx-lifecycle-runtime-compose", "androidx-lifecycle-viewmodel-ktx", "androidx-lifecycle-viewmodel-compose", "androidx-navigation-compose", "androidx-datastore-preferences", "androidx-core-splashscreen", "androidx-swiperefreshlayout", "androidx-browser"] ... [plugins] android-application = { id = "com.android.application", version.ref = "agp" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// build.gradle.kts(Project層) ... // buildscript { ... repositories { google() mavenCentral() } ... } plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.dagger.hilt.android.plugin) apply false alias(libs.plugins.firebase.crashlytics) apply false ... } ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
// build.gradle.kts(Module層) ... plugins { alias(libs.plugins.android.application) alias(libs.plugins.compose.compiler) alias(libs.plugins.dagger.hilt.android.plugin) alias(libs.plugins.firebase.crashlytics) alias(libs.plugins.firebase.perf) ... } ... dependencies { // Kotlin implementation(libs.bundles.kotlin) // Compose implementation(platform(libs.androidx.compose.bom)) implementation(libs.bundles.compose) debugImplementation(libs.bundles.compose.debug) androidTestImplementation(libs.ui.test.junit4) // AndroidX implementation(libs.bundles.androidx) ... // Room implementation(libs.bundles.room) ksp(libs.androidx.room.compiler) ... } |
今後は Version Catalog の [versions]
セクションにバージョン情報を集約しているため、ライブラリのアップデートと管理がより簡単になりました。
![](https://engineering.nifty.co.jp/wp-content/uploads/2024/06/12fc24410136744cd684a74c76258a7a-1024x307.png)
ぜひ Version Catalog の便利さを体験してみましょう!
![](https://engineering.nifty.co.jp/wp-content/uploads/2023/08/hiring.png)
ニフティでは、
さまざまなプロダクトへ挑戦する
エンジニアを絶賛募集中です!
ご興味のある方は以下の採用サイトより
お気軽にご連絡ください!
Tech TalkやMeetUpも開催しております!
こちらもお気軽にご応募ください!
connpassでニフティグループに
参加いただくと
イベントの
お知らせが届きます!