◉ANTIGRAVITY LABEN
記事一覧/Antigravity 基本
◉ Antigravity 基本/2026-10-20中級

古い Android 端末だけが落ちる NoClassDefFoundError — エージェントに渡す前に私が揃える 4 つの手がかり

最新の端末では動くのに、Android 6 の端末だけがクラッシュする。Glide を上げた後に起きた Supplier の NoClassDefFoundError を例に、原因の切り分け順、coreLibraryDesugaring の設定、エージェントに調査を任せるときに先に揃えておく手がかりをまとめます。

antigravity468android30desugaringcrashlytics6troubleshooting92

Crashlytics の画面を朝いちばんに開いたとき、見慣れないスタックトレースが一行だけ増えていました。NoClassDefFoundError: java.util.function.Supplier。手元のエミュレータでは再現せず、発生している端末を絞ると、Android 6.0.1 のユーザーにほぼ限られていました。

私は個人開発で Android アプリを長く運用しておりますが、画像ライブラリを更新した直後にこの種のクラッシュが出ると、まず「自分のコードのどこかか」と身構えてしまいます。実際には、自分のコードは一行も触っていませんでした。

最初にお伝えしたいのは、原因は新しい端末で動く環境の側にあり、直し方は設定の 1 行だった、ということです。ここでは、そこへ辿り着くまでの切り分けの順番と、エージェントに調査を頼むときに私が先に揃えるようにしている手がかりを書き残します。

症状の見分け方 — 「古い OS だけ」「起動直後ではない」「自分のクラスではない」

エラー文言だけでも、見る場所はかなり絞れます。

手がかりこの件での値何を意味するか
例外の種類NoClassDefFoundErrorコンパイル時には存在したクラスが、実行する端末の OS に無い
欠けているクラスjava.util.function.SupplierJava 8 で入った標準 API。Android では API 24(7.0)以降の OS にしか入っていない
発生する端末Android 6.0.1(API 23)API 24 未満の端末だけが落ちる
スタックの先頭依存ライブラリ側のパッケージ自分のコードが直接呼んだのではなく、ライブラリ内部で使われている

この 4 行がそろうと、「ライブラリが Java 8 の API を使うようになったのに、アプリ側がそれを古い OS 向けに置き換えていない」という仮説が最有力になります。逆に、ClassNotFoundException や自分のパッケージ名が先頭に出る場合は、別の原因を疑います。

原因の切り分けは、この順番で

私は次の順で確かめています。上から順に、安いものを先にです。

  1. minSdk を確認する。24 未満なら、Java 8 の標準 API を OS 任せにできません
  2. 脱糖(desugaring)の設定が入っているか確認する。coreLibraryDesugaringEnabled が無ければ、ここが欠けています
  3. どの依存ライブラリが Supplier を参照しているか確認する。更新したライブラリが犯人かどうかを、バイトコードで確かめます
  4. 直したあと、古い OS の実機かエミュレータで再現を潰す

3 番目は、次のような小さなスクリプトで確かめられます。クラスファイルの定数プールには文字列がそのまま入っているため、jar を展開せずに grep できます。

#!/usr/bin/env bash
# 何を解決するか: 依存ライブラリの jar のうち、java.util.function.Supplier を参照しているものを洗い出す
# 使い方: ./find-supplier-users.sh path/to/jars/*.jar
set -euo pipefail
 
for jar in "$@"; do
  # クラスファイルの中身を連結して、参照文字列の出現数を数える
  hits=$(unzip -p "$jar" '*.class' 2>/dev/null | grep -a -c 'java/util/function/Supplier' || true)
  if [ "${hits:-0}" -gt 0 ]; then
    echo "${hits}	${jar}"
  fi
done | sort -rn

ここで出てきた jar が、更新したライブラリと一致すれば、仮説はほぼ確定です。なぜ jar を直接見るのかというと、Gradle の依存ツリーだけでは「どのライブラリがどの API を使っているか」までは分からないからです。

直し方 — coreLibraryDesugaring を有効にする

minSdk が 24 未満のアプリでは、java.util.function のような Java 8 の標準ライブラリを、ビルド時にアプリ側へ取り込む設定があります。Groovy の build.gradle では次のとおりです。

android {
    compileOptions {
        // 古い OS に無い Java 8+ の標準 API を、ビルド時に取り込む
        coreLibraryDesugaringEnabled true
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}
 
dependencies {
    // バージョンは公式ドキュメントで現行版を確認してから決める
    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
}

Kotlin DSL の場合は isCoreLibraryDesugaringEnabled = true と書きます。この設定は、アプリのコードだけでなく、依存ライブラリのバイトコード内の参照も書き換えてくれます。だから、ライブラリ側を直せなくても、アプリ側の 1 行で穴が塞がるのです。

私の場合は、この設定を足してビルドし直すだけで、Android 6.0.1 のクラッシュが止まりました。リリースは段階公開にして、Crashlytics で同じ例外が消えていくのを数日かけて見届けております。

エージェントに調査を頼むとき、先に揃える 4 つの手がかり

ここからが、この記事で一番お伝えしたかった部分です。

この件を最初に見たとき、私はスタックトレースを丸ごとエージェントに貼り、「原因を調べて」とだけ頼みかけました。ただ、それだと、エージェントは新しい端末での挙動を前提に推測を始めてしまいます。結果は芳しくなく、依存ライブラリの変更履歴を延々と読む方向へ流れてしまいました。いま思えば、足りなかったのはエージェントの力ではなく、私の側の下ごしらえでした。

いまは、依頼の前に次の 4 つを私の側で揃えるようにしています。

  1. 端末の OS バージョンと API レベル。「Android 6.0.1(API 23)のみ」と明記します
  2. minSdk と、desugaring 設定の有無。build.gradle の該当箇所をそのまま貼ります
  3. 直前に更新した依存ライブラリと、更新前後のバージョン。差分が小さいほど、調べる範囲が狭まります
  4. 再現できた環境と、再現できなかった環境。「エミュレータの API 34 では再現せず」も立派な手がかりです

こうして渡すと、エージェントの最初の返答が「minSdk が 24 未満なのに脱糖が無い」という方向から始まるようになりました。原因を探すより、何が欠けているかの見取り図を先に渡すほうが、はるかに短く済みます。

エージェントに渡すのは、症状ではなく、欠けているものの輪郭。 この線引きは、体調の悪い日でも崩さないようにしています。

直したあとの確かめ方

直したつもりで終わらせないために、次の 2 点だけは確かめています。

  • 古い OS の環境で、同じ画面遷移を一度通す。API 23 のエミュレータで十分です
  • リリースビルドで確認する。デバッグビルドでは通っても、R8 が絡むリリースビルドで挙動が変わることがあります

どちらも地味な作業ですが、ここを省くと、次の更新でまた同じ朝を迎えることになります。

次にやること

お手元のアプリで、まず minSdk の値を見てみてください。24 未満であれば、coreLibraryDesugaringEnabled が入っているかを確認するところから始めていただければと思います。私も、ライブラリを更新するたびに、この 2 か所を見る習慣にしております。

シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

◉ Antigravity 基本2026-10-17
質問を書く前に、環境メモを1分で作っておく — 返事が付きやすい投稿のための小さなシェルスクリプト
フォーラムや Issue に質問を書く前に、OS・バージョン・ログ末尾を1つのメモにまとめて貼れるようにする小さなシェルスクリプトを用意しました。伏せ字の範囲、書き込みに使う一文の型、スクリプトが拾えないものまで、動かして確かめた範囲で書き残します。
◉ Antigravity 基本2026-09-15
エージェントが理由を残さず終わるとき、読ませたページの文字コードを疑っています
ReadURL に渡したページが原因で、Antigravity のエージェントが Agent execution terminated だけを残して終わることがあります。文字コードを宣言しない古いサイトに当たったときの切り分けと、先に取得して渡す運用への切り替え方を書き残します。
◉ Antigravity 基本2026-04-28
Antigravity で「Run command?」が何度も出る・Always Proceed でも止まるときの原因と対処
Antigravity でコマンド承認のダイアログが繰り返し出る、許可リストに入れたのに止まる、Always Proceed でも確認される。公式の設定仕様と、フォーラムで報告されている既知の不具合を分けて、切り分けの順番と現実的な回避策を整理しました。