Beautiful HD Wallpapers(iOS/Android、累計 5,000 万 DL 超)の v2.0.0 をリリースした直後、Google Play Console のクラッシュレポートに見慣れないエラーが大量に流れ込んできました。28 日間で 50 名以上のユーザーが同じ画面でアプリを落としていて、共通点は Android 6.0.1 と 7.x の端末だけだということでした。
最新端末では全く再現せず、エラーログは一見すると全く関係ない場所を指していました。Antigravity にそのままスタックトレースを貼っても「Glide の設定を見直してみてください」という的外れな提案が返ってくることもありました。個人開発を続けていると、こういった問題に遭遇したとき、真っ先に「自分が書いたコードのバグ」を疑ってしまいますが、原因は全く別のところにありました。
根本原因は Java 8 の API クラスをネイティブサポートしていない旧端末への対応設定が欠けていたこと。そしてその設定は、ライブラリのバージョンアップを Antigravity に依頼した際に追加されなかったものでした。
同じ問題で時間を溶かしている方のために、診断から修正、再発防止まで整理します。
エラーログはこういう形になります
Play Console または Crashlytics に以下のようなスタックトレースが現れます。
java.lang.NoClassDefFoundError: Failed resolution of: Ljava/util/function/Supplier;
at com.bumptech.glide.load.engine.cache.DiskCacheAdapter.<init>(DiskCacheAdapter.java:14)
at com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory.build(...)
java.util.function.Supplier は Java 8 の標準ライブラリに含まれるクラスです。Android 6.x/7.x の端末は Java 8 の一部 API をネイティブには持っておらず、このクラスが見つからずにクラッシュします。
なぜ Antigravity は警告してくれないのか
Antigravity は build.gradle を読んで「Glide 5.0.5 を追加すればよい」と提案しますが、minSdkVersion が 23(Android 6.0)のプロジェクトで Java 8 API を安全に使うには追加の設定が必要という文脈まで自動的に判断できない場合があります。
Glide 5.x は Glide 4.x と比べて内部実装が Java 8 の Functional Interface に大きく依存するように変わっています。AGP(Android Gradle Plugin)9.x 以降でも、この互換性ブリッジ(デシュガーリング)は明示的に有効化しなければなりません。
私自身の経験では、Antigravity に「Glide を 5 系にアップグレードしてください」と依頼したときに coreLibraryDesugaring の設定を追記してくれなかったことが発端でした。ライブラリを更新するタスクと、minSdkVersion の互換性を保つタスクは、AI にとって別の判断として扱われることがあります。
Glide 以外にも、以下のライブラリが Java 8 API に依存しており、同様のクラッシュを引き起こす可能性があります。
- OkHttp 4.x 以降:
java.net.InetAddressまわりの変更 - Retrofit 2.8.x 以降:
java.util.function.Functionの利用 - Kotlin Coroutines の特定バージョン:
java.util.concurrent.Flowへの依存 - Room 2.5.x 以降:
java.util.Optionalの利用
廣川が運営するアプリでは minSdkVersion を長期間 23 に維持しており、アップデートのたびにこの問題が潜在的に発生しうる状態でした。一度の見落としが数十人規模のクラッシュに直結したため、以来、ライブラリ更新時の確認項目として必ず意識するようにしています。
診断:本当に desugaring の問題か確認する
まずエラーログで以下のキーワードが含まれているか確認してください。
NoClassDefFoundError: Ljava/util/function/
NoClassDefFoundError: Ljava/util/stream/
NoClassDefFoundError: Ljava/time/
これらはいずれも Java 8 の API クラスです。java.util.function.*、java.util.stream.*、java.time.* のいずれかが原因であれば、デシュガーリングの設定が欠けている可能性が高いです。
クラッシュが API レベル 26 未満の端末だけに集中していれば、ほぼ確定だと思います。
Play Console では「Android バイタル」→「クラッシュとアプリケーションが応答しない(ANR)」から端末ごとのクラッシュ分布を確認できます。旧端末に偏った分布が見えたら、デシュガーリングの問題を真っ先に疑ってください。
修正:coreLibraryDesugaring を有効化する
app/build.gradle に以下を追加します。
android {
compileOptions {
// Java 8 API を古い Android 端末でも使えるようにする
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
// デシュガーリング用ライブラリ(AGP 9.x 推奨バージョン)
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'
}Kotlin DSL(build.gradle.kts)の場合は次のように書きます。
android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
}この修正を v2.1.0 に入れてリリースしたところ、28 日間続いていたクラッシュが次のビルドから消えました。Android 6.0.1 ユーザー全員のクラッシュが 1 行の設定追加で解消できたことは、今でも印象に残っています。
Antigravity に最初から伝える方法
同じ問題を再発させないために、プロジェクトの GEMINI.md(または Antigravity のカスタムインストラクション)に以下を追記しておくことをおすすめします。
## Android プロジェクトの制約
- minSdkVersion は 23(Android 6.0)を維持すること
- Java 8 API を使うライブラリを追加・更新する際は、
必ず coreLibraryDesugaring の設定を確認・追記すること
- Glide 5.x、OkHttp 4.x 以降、Kotlin Coroutines の最新版は
Java 8 API に依存しているためデシュガーリングが必須こうしておくことで、Antigravity がライブラリを追加・更新するたびに coreLibraryDesugaring の存在を意識した提案をしてくれるようになります。アーティストとしての活動とアプリ開発を並行している私の場合、開発の中断を最小限にしたいという動機から、こういった「一度設定すれば再発しない」対策を積極的に GEMINI.md に蓄積するようにしています。
Before/After で確認する
修正前(クラッシュする状態):
android {
compileOptions {
// coreLibraryDesugaringEnabled の記述なし
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'com.github.bumptech.glide:glide:5.0.5'
// coreLibraryDesugaring の指定なし ← ここが問題
}修正後(Android 6.x でも正常動作):
android {
compileOptions {
coreLibraryDesugaringEnabled true // ← この1行を追加
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'com.github.bumptech.glide:glide:5.0.5'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4' // ← 追加
}AGP バージョンと desugar_jdk_libs のバージョン確認
desugar_jdk_libs のバージョンと AGP のバージョンには対応関係があります。AGP 9.x 以降では 2.x.x 系を使うのが推奨されています。古い 1.x.x 系では一部の Java 11 API が含まれていないため、使えるクラスに制限があります。
# 現在の AGP バージョンを確認
grep "com.android.tools.build:gradle" build.gradle.kts最新の対応バージョンは google/desugar_jdk_libs の GitHub Releases で確認できます。
旧端末でのテスト体制を整える
デシュガーリング関連のバグを事前に防ぐには、API レベル 23〜25 の実機か、対応する Android エミュレーターで定期的にテストしておく点が肝心です。
Android Studio の AVD Manager では API Level 23(Android 6.0 Marshmallow)のエミュレーターを作成できます。ライブラリをアップグレードしたときに最低限このエミュレーターで起動確認することで、リリース後のクラッシュをかなり予防できます。
個人開発では実機のバリエーションを揃えるのが難しい場合もありますが、Google Play Console の「事前起動レポート」を活用することも選択肢の一つです。Google が Firebase Test Lab を使って複数端末で自動テストを実行し、クラッシュを検出してくれます。
minSdkVersion を確認して 26 未満であれば、ライブラリ更新前に必ず coreLibraryDesugaring の有無を確認する習慣をつけてください。
このパターンを一度経験してから GEMINI.md に記載するようにしたため、その後の各アプリの更新では同種のクラッシュは発生していません。アプリ開発を長く続けていると、「一度踏んだ落とし穴は必ず記録する」という習慣が自然と身につきます。GEMINI.md はそのための最適な場所だと感じています。
個人開発において「設定 1 行の落とし穴」は意外と多く、どれも原因さえわかれば修正は一瞬ですが、たどり着くまでに時間がかかることがほとんどです。同じ問題で詰まっている方の参考になれば幸いです。