はじめに
Android アプリ開発をしていて、「なんでビルドがこんなに遅いんだ...」「またビルドエラーが...」と頭を抱えたことはありませんか?
私も以前は AGP のアップデートを避けて古いバージョンを使い続けていたのですが、あるプロジェクトでビルド時間が 15分 を超えるようになってしまい、流石にこのままではいけないと思いました。
そこから本格的に Android Gradle Plugin (AGP) のバージョン管理と最適化に取り組んだ結果、ビルド時間を3分まで短縮し、開発体験が劇的に改善されました。
今回は、そんな実際の開発現場で培った AGP の管理手法と最適化テクニックについて、失敗談も交えながら詳しくお話しします。
まず知っておきたい!2025年の AGP 事情
「月次リリースって何?」から始まった調査
2025年から AGP が 月次リリース を開始したのをご存知ですか?
私も最初は「え、そんなに頻繁に更新されても追いつけない...」と思ったのですが、実際に使ってみると小刻みな改善が開発体験を大幅に向上させてくれることがわかりました。
現在利用できる AGP のバージョンはこちらです:
| AGP バージョン | リリース日 | 対応 API Level | 主な特徴 |
|---|---|---|---|
| 8.13 | 2025年9月 | API 36 | 最新安定版 |
| 8.11.0 | 2025年6月 | API 36 | 長期サポート版 |
| 8.10.0 | 2025年5月 | API 36 | パフォーマンス改善 |
| 8.9.0 | 2025年3月 | API 35 | Configuration Cache 安定化 |
| 8.8.0 | 2025年1月 | API 35 | 月次リリース開始 |
「やっとセマンティック バージョニングに!」
これまで AGP のバージョン管理って正直よくわからなかったのですが、2025年からセマンティック バージョニングを正式採用してくれました。
これにより、「このアップデートは破壊的変更があるのか?」「安全にアップデートできるのか?」が一目でわかるようになりました。
// build.gradle.kts(Project レベル)
plugins {
id("com.android.application") version "8.13.0" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
}
月次リリースが実際どうなのか使ってみた感想
Android Studio の月次リリースサイクルに合わせて、AGP も月次での機能追加・改善が行われるようになりました。
実際に使ってみた感想:
- ✅ 小さな改善が積み重なって開発が快適に
- ✅ 大きな破壊的変更が少なくなった
- ⚠️ ただし、企業のプロジェクトでは月次更新は現実的でない場合も
個人開発なら積極的に、チーム開発なら四半期ごとなど計画的に更新するのがおすすめです。
「バージョンの組み合わせがよくわからない」問題を解決
AGP と Gradle の互換性でハマった話
「This version of the Android Gradle plugin requires...」このエラー、見たことありませんか?
私も最初の頃はこのエラーが出るたびに Google で検索していたのですが、以下の表を作ってからは迷うことがなくなりました:
| AGP バージョン | 最小必要 Gradle バージョン | 推奨 Gradle バージョン | JDK 要件 |
|---|---|---|---|
| 8.13.0 | 8.10.2 | 8.13.0 | JDK 17-24 |
| 8.10.0-8.11.0 | 8.10.0 | 8.11.0 | JDK 17-24 |
| 8.8.0-8.9.0 | 8.8.0 | 8.9.0 | JDK 17-22 |
| 8.6.0-8.7.0 | 8.6.0 | 8.7.0 | JDK 17-21 |
Gradle Wrapper の更新でよくやらかすミス
あるあるなミス: 「gradlew」 ファイルを直接編集してしまうこと
正しくはこのコマンドで更新しましょう:
# Gradle Wrapper の更新(推奨手法)
./gradlew wrapper --gradle-version 8.13.0
Kotlin との互換性で注意したいこと
Kotlin Multiplatform や Compose Compiler を使っている場合は特に注意が必要です:
| Kotlin バージョン | 対応 Gradle バージョン | 備考 |
|---|---|---|
| 2.1.0-2.1.10 | 8.6-8.13 | 完全互換 |
| 2.0.20-2.0.21 | 8.6-8.10 | 部分的制限あり |
バージョンカタログ
以前はマルチモジュールプロジェクトで「あのライブラリのバージョン、どこで定義したっけ?」という状態が日常茶飯事でした。
しかし、バージョンカタログ を使うようになってから、この悩みが一気に解決しました!
2025年現在、Android Studio の新規プロジェクトでは バージョンカタログ がデフォルトで採用されています。
# gradle/libs.versions.toml
[versions]
agp = "8.13.0"
kotlin = "2.1.0"
core-ktx = "1.15.0"
lifecycle = "2.8.5"
compose-bom = "2025.09.00"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
androidx-lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
[bundles]
lifecycle = ["androidx-lifecycle-runtime", "androidx-lifecycle-viewmodel"]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
バージョンカタログを導入するとマルチプロジェクトで効果を発揮します。今までgradleごとに定義していたバージョンがバージョンカタログで一元管理ができるようになりました。
// app/build.gradle.kts
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.bundles.lifecycle)
implementation(platform(libs.androidx.compose.bom))
}
// feature/login/build.gradle.kts
dependencies {
implementation(libs.androidx.core.ktx) // 同一バージョンが保証される
implementation(libs.bundles.lifecycle)
}
BOM (Bill of Materials) 、最初は「なにこのよくわからない機能...」と思っていたのですが、使ってみたら Compose 関連ライブラリのバージョン管理地獄 から解放されました:
[versions]
compose-bom = "2025.09.00"
okhttp-bom = "4.13.0"
[libraries]
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
okhttp-bom = { group = "com.squareup.okhttp3", name = "okhttp-bom", version.ref = "okhttp-bom" }
ビルド時間 15分 → 3分に短縮した最適化テクニック
Configuration Cache の衝撃的な効果
フルビルドが 15分かかっていたのが、Configuration Cache を有効化しただけで 3分 まで短縮されました。
Configuration Cache はビルドタスクグラフをキャッシュして、変更のない部分の再利用を可能にします:
# gradle.properties
org.gradle.configuration-cache=true
org.gradle.configuration-cache.problems=warn
最適化したつもりでも、実際に効果があるかは計測しないとわかりません。このコマンドで定期的にチェックしましょう:
# ビルド時間の詳細分析
./gradlew assembleDebug --profile --build-cache
# Configuration Cache の効果確認
./gradlew clean assembleDebug # 初回(キャッシュなし)
./gradlew assembleDebug # 2回目(キャッシュ利用)
失敗しないアップグレード戦略
🔍 フェーズ1: 「敵を知る」事前調査
アップグレードを始める前に、現状を把握することが成功の鍵です。私も以前は「とりあえず最新版に上げてみよう」と無計画に進めて失敗したことがあります。
まずは現在の環境を正確に把握しましょう:
- 現在使用している AGP、Gradle、Kotlin のバージョン
- プロジェクトで使用している主要ライブラリとそのバージョン
- 既知の互換性問題や deprecation 警告
# 現在のバージョン確認
./gradlew -v
# 利用可能なアップデート確認
./gradlew dependencyUpdates
Note: dependencyUpdates タスクを利用するには、com.github.ben-manes.versions プラグインの導入が必要です。以下のようにプロジェクトルートの build.gradle.kts に追加します。
// build.gradle.kts (Project レベル)
plugins {
alias(libs.plugins.versions) apply false
}
# gradle/libs.versions.toml
[versions]
versions-plugin = "0.51.0"
[plugins]
versions = { id = "com.github.ben-manes.versions", version.ref = "versions-plugin" }
💪 フェーズ2: 「一気にやらない」段階的更新
「一気に全部アップデートすれば早く終わる」と考えがちですが、これは最大の落とし穴です。問題が起きたときに原因の特定が非常に困難になります。
推奨する更新順序:
-
まず Gradle バージョンのみ更新
- この段階でビルドが通ることを確認
- Configuration Cache や Build Cache の設定も見直す良いタイミング
# gradle/wrapper/gradle-wrapper.properties
distributionUrl=https://services.gradle.org/distributions/gradle-8.10.0-bin.zip
-
次に AGP バージョンを更新
- 一度に飛ばすのは1つのメジャーバージョンまで
- 例: 7.4.x → 8.0.x は OK、7.4.x → 8.2.x は避ける
# libs.versions.toml
agp = "8.10.0" # 一度に1つのメジャーバージョンまで
-
最後に Kotlin と依存ライブラリを更新
- AGP との互換性を確認してから
- 主要ライブラリから順番に
🧪 フェーズ3: 「本当に大丈夫?」影響範囲の検証
アップグレード後に「ビルドは通ったけど、実はバグが混入していた」という経験はありませんか?私は以前、リリース直前にこれで大慌てしました。
段階的な検証チェックリスト:
-
ビルドの成功確認
- Debug と Release の両方でビルドが通るか
- ProGuard/R8 の警告やエラーが出ていないか
-
自動テストの実行
- Unit Test で既存機能が壊れていないか
- UI Test で画面遷移や主要フローが正常か
-
実機での動作確認
- 主要な機能が正常に動作するか
- パフォーマンスに劣化がないか
- メモリリークやクラッシュが発生していないか
# ビルドエラーの確認
./gradlew assembleDebug
# テストスイートの実行
./gradlew testDebugUnitTest
./gradlew connectedAndroidTest
# Lint チェックも忘れずに
./gradlew lint
私が必ずチェックする項目:
- アプリの起動時間
- 主要画面の表示速度
- API 通信の正常性
- ローカルデータベースの読み書き
実践的な最適化手法
依存関係の最適化
不要な依存関係の除去
# 未使用依存関係の検出
./gradlew app:dependencies --configuration releaseRuntimeClasspath
ProGuard/R8 の最適化設定
ProGuard/R8 は APK サイズの削減とコード難読化を行う重要なツールですが、設定を誤るとアプリがクラッシュする原因になります。
R8 の最適化で私が経験した失敗談:
以前、リフレクションを使っているクラスの keep ルールを書き忘れて、リリースビルドだけがクラッシュするという事態に...。Debug ビルドでは発見できないので、必ず Release ビルドでのテストを忘れずに。
# proguard-rules.pro
# Kotlin Coroutines の警告を抑制
-dontwarn kotlinx.coroutines.debug.**
# Lifecycle コンポーネントの保持(リフレクションで使用される)
-keep class androidx.lifecycle.** { *; }
-keepclassmembers class * extends androidx.lifecycle.ViewModel {
<init>(...);
}
# データクラスの保持(Gson/Moshi などで使用)
-keep class com.example.app.data.model.** { *; }
# Retrofit のインターフェースを保持
-keep interface com.example.app.data.api.** { *; }
# R8 フルモードでの最適化設定
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
R8 フルモードを有効にすると、さらなる最適化が可能です:
# gradle.properties
android.enableR8.fullMode=true
ビルドバリアントの最適化
開発中は「とにかく速くビルドしたい」、リリース時は「とにかく品質とサイズを最適化したい」という相反する要求があります。ビルドバリアントを適切に設定することで、この両立が可能になります。
以前は全てのビルドタイプで同じ設定を使っていましたが、Debug ビルドでも ProGuard を有効にしていたため、ビルド時間が非常に長く開発効率が悪化していました。Debug と Release で設定を分けることで、開発速度を保ちながら品質も確保できるようになりました。
android {
buildTypes {
debug {
// 開発速度を最優先
isDebuggable = true
isMinifyEnabled = false
isShrinkResources = false
applicationIdSuffix = ".debug"
versionNameSuffix = "-debug"
// Debug ビルドではマルチ DEX を自動有効化
isMultiDexEnabled = true
// Debug では最適化をスキップしてビルド時間短縮
proguardFiles(
getDefaultProguardFile("proguard-android.txt")
)
}
release {
// 本番品質を最優先
isDebuggable = false
isMinifyEnabled = true
isShrinkResources = true
// コード最適化と難読化を有効化
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
// APK の署名設定
signingConfig = signingConfigs.getByName("release")
}
// ステージング環境用のビルドタイプ(オプション)
create("staging") {
initWith(getByName("release"))
applicationIdSuffix = ".staging"
versionNameSuffix = "-staging"
isDebuggable = true // ステージングではデバッグ可能に
}
}
// フレーバー別の設定(無料版/有料版など)
flavorDimensions += "version"
productFlavors {
create("free") {
dimension = "version"
applicationIdSuffix = ".free"
}
create("paid") {
dimension = "version"
applicationIdSuffix = ".paid"
}
}
}
ビルドバリアントごとの特性:
| ビルドタイプ | ビルド時間 | APK サイズ | デバッグ可能 | 用途 |
|---|---|---|---|---|
| Debug | 高速 | 大きい | ✅ | 日常開発 |
| Staging | 中速 | 小さい | ✅ | QA テスト |
| Release | 低速 | 最小 | ❌ | 本番リリース |
モジュール分割による最適化
大規模プロジェクトでは、モジュール分割がビルド時間短縮とコードの保守性向上の両方に効果を発揮します。
あるプロジェクトで単一モジュールから機能別モジュールに分割したところ、インクリメンタルビルドが 12分→4分 に短縮されました。変更したモジュールだけが再ビルドされるため、開発効率が劇的に向上しました。
モジュール分割の原則:
- レイヤーごとに分割 - クリーンアーキテクチャに沿った構成
- 機能ごとに分割 - 各機能を独立したモジュールに
- 依存関係を一方向に - 循環依存を避ける
app/ # アプリケーションモジュール(統合層)
├── core/
│ ├── ui/ # 共通 UI コンポーネント
│ ├── network/ # ネットワーク共通処理
│ └── database/ # データベース共通処理
├── feature/
│ ├── login/ # ログイン機能モジュール
│ ├── dashboard/ # ダッシュボード機能モジュール
│ ├── profile/ # プロフィール機能モジュール
│ └── settings/ # 設定機能モジュール
├── data/ # データレイヤー(リポジトリ実装)
└── domain/ # ドメインレイヤー(ビジネスロジック)
※ 各モジュールに個別の build.gradle.kts を作成します
依存関係の設定例:
// app/build.gradle.kts(アプリケーションモジュール)
dependencies {
implementation(project(":feature:login"))
implementation(project(":feature:dashboard"))
implementation(project(":feature:profile"))
implementation(project(":core:ui"))
}
// feature/login/build.gradle.kts(機能モジュール)
dependencies {
implementation(project(":core:ui"))
implementation(project(":domain"))
implementation(project(":data"))
}
// data/build.gradle.kts(データレイヤー)
dependencies {
implementation(project(":domain"))
implementation(project(":core:network"))
implementation(project(":core:database"))
}
モジュール分割のメリット:
- ✅ ビルド時間の短縮 - 変更したモジュールのみ再ビルド
- ✅ 並列ビルドの効率化 - 依存関係のないモジュールは同時にビルド
- ✅ コードの責任分離 - 各モジュールの役割が明確に
- ✅ テストの高速化 - モジュール単位でのテストが可能
- ✅ 再利用性の向上 - 他のプロジェクトでもモジュールを利用可能
並列ビルドを有効にする設定:
# gradle.properties
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configureondemand=true
注意点:
- 過度なモジュール分割は逆効果(管理コストが増加)
- 小規模プロジェクトでは単一モジュールの方がシンプル
- 機能追加の際にモジュール構成を見直すことも重要
まとめ
長々と書いてきましたが、結局 AGP と上手く付き合う ために大切なことはこの5つです:
1️⃣ 急がば回れ - 段階的アップグレード
「一気に最新へ!」は禁物です。経験した8ヶ月の地獄を繰り返したくなければ、一つずつ着実に。
2️⃣ バージョンカタログ は神 - 一元管理の威力
マルチモジュールプロジェクトでは本当にゲームチェンジャーでした。これなしではもう作業できません。
3️⃣ Configuration Cache は必須 - 15分が3分に
ビルド時間の革命でした。AGP 8.1+ なら安定版で使えます。使わない理由がありません。
4️⃣ バックアップは絶対 - 取り返しのつかない作業
アップグレード作業は必ず別ブランチで。これだけで精神的負担が大幅軽減されます。
5️⃣ チーム共有は重要 - 独断はダメ、絶対
「勝手にアップグレードしておいた」はチームを崩壊させます。事前の相談と計画共有はマストです。