RORK LABEN
ACQUISITION — Rorkが初の買収を実施。macOSでネイティブSwiftアプリを生成するPaperlineを取得しましたFUNDING — Left Lane Capital主導の$15Mシードは、AI時代のモバイルアプリの作り方と収益化の再定義に充てられますGROWTH — Rork Maxはローンチから3日でARR $1.5Mに到達し、2週間で年間売上を倍増させたとされますENGINE — Rork MaxはClaude Code+Claude Opus 4.6駆動。Web初のSwiftビルダーとしてXcodeを置き換えますSPLIT — 通常RorkはReact Native(Expo)、Rork MaxはネイティブSwiftでAppleエコシステム全域が対象ですPRICING — 無料で開始でき、有料は月25ドル〜、Rork Maxは月200ドルですACQUISITION — Rorkが初の買収を実施。macOSでネイティブSwiftアプリを生成するPaperlineを取得しましたFUNDING — Left Lane Capital主導の$15Mシードは、AI時代のモバイルアプリの作り方と収益化の再定義に充てられますGROWTH — Rork Maxはローンチから3日でARR $1.5Mに到達し、2週間で年間売上を倍増させたとされますENGINE — Rork MaxはClaude Code+Claude Opus 4.6駆動。Web初のSwiftビルダーとしてXcodeを置き換えますSPLIT — 通常RorkはReact Native(Expo)、Rork MaxはネイティブSwiftでAppleエコシステム全域が対象ですPRICING — 無料で開始でき、有料は月25ドル〜、Rork Maxは月200ドルです
記事一覧/アプリ開発
アプリ開発/2026-06-25中級

リリースビルドだけ落ちる — Expo(Android) で R8 が剥がしたクラスを keep ルールで救う

AAB を小さくしようと R8 のコード圧縮を有効化したら、本番だけ特定画面でクラッシュ。剥がされたクラスを mapping.txt から突き止め、expo-build-properties で keep ルールを当てるまでの手順をまとめました。

Expo102Android43R8ProGuardトラブルシューティング74

ストアの審査も通り、配信も始まったアプリで、ある画面を開いた瞬間だけ落ちる、という報告が一件だけ届きました。手元の開発ビルドでは何度開いても再現しません。Expo Go でも、expo run:android のデバッグビルドでも素通りします。落ちるのは、Play からダウンロードした本番の AAB だけでした。

原因は、直前に「アプリサイズを少しでも削りたい」と思って有効化した R8 のコード圧縮でした。R8 が「どこからも参照されていない」と判断したクラスを取り除いた結果、実行時にそのクラスを探して見つからず落ちていたのです。同じ轍を踏まないよう、症状の見分け方と keep ルールの当て方を残しておきます。

デバッグでは出ず本番だけ落ちる、という症状

このクラッシュには、はっきりした手触りがあります。

開発中は一切再現しません。minifyEnabled が無効なデバッグビルドではコードがそのまま残るため、剥がされるクラスが存在しないからです。圧縮が走るのはリリースビルドだけなので、症状もリリースビルドだけに現れます。

クラッシュログを見ると、例外は ClassNotFoundExceptionNoSuchMethodError、あるいは Kotlin の NullPointerException として出ます。そしてスタックトレースのクラス名が a.a.b のような短い記号に置き換わっています。この難読化された名前こそ、R8 が手を入れた印です。

特定の画面・特定の機能だけで落ちるのも特徴です。アプリ全体ではなく、リフレクションや JSON のデシリアライズを使っている箇所に偏ります。

なぜ参照しているクラスが消えるのか

R8 は、コードを静的にたどって「使われているか」を判定します。通常のメソッド呼び出しは追跡できますが、文字列のクラス名から実体を取り出すリフレクションや、ネイティブ側から呼ばれるブリッジ、JSON ライブラリがフィールド名をもとに値を詰めるモデルクラスは、静的解析からは「誰も呼んでいない」ように見えます。

そのため、実際には実行時に必要なクラスやフィールドが、未使用とみなされて削除されたり、名前を短く付け替えられたりします。Gson のようなライブラリがフィールド名に依存している場合、名前が変われば対応が取れず、値が空になったり例外になったりします。

つまり「コードが間違っている」のではなく、「R8 に必要だと伝えられていない」状態です。直し方は、消してほしくない部分を keep ルールで明示することに尽きます。

剥がされたクラスを mapping.txt で突き止める

闇雲に keep を足す前に、何が剥がされたのかを特定します。R8 はビルドのたびに難読化の対応表を出力します。

android/app/build/outputs/mapping/release/mapping.txt

このファイルには「難読化後の名前 → 元の名前」の対応が入っています。EAS Build で作った場合は、ビルドの成果物(Artifacts)から同じ mapping.txt を取得できます。

クラッシュした端末のスタックトレースに出ている a.a.b のような記号を、mapping.txt で逆引きすると、元のクラス名が分かります。Google Play Console を使っている場合は、この mapping.txt をアプリのバージョンに紐づけてアップロードしておくと、Console 上のクラッシュレポートが自動で元の名前に復元されて読めるようになります。私はこれを忘れて、記号の羅列のまま数十分悩みました。

元のクラス名さえ分かれば、あとはそのクラスが「なぜ静的解析から見えなかったのか」を考えます。多くはリフレクション経由か、シリアライズ用のモデルです。

expo-build-properties で keep ルールを当てる

Expo の管理ワークフローでは、android/ を直接編集しても prebuild で上書きされます。R8 の設定や keep ルールは expo-build-properties プラグイン経由で渡します。

剥がされたモデルクラス群を残すには、パッケージ単位で keep します。

{
  "expo": {
    "plugins": [
      [
        "expo-build-properties",
        {
          "android": {
            "enableProguardInReleaseBuilds": true,
            "enableShrinkResourcesInReleaseBuilds": true,
            "extraProguardRules": "-keepattributes Signature\n-keepattributes *Annotation*\n-keep class com.yourapp.models.** { *; }"
          }
        }
      ]
    ]
  }
}

com.yourapp.models.** は、自分のアプリの実際のパッケージ名に置き換えてください。-keepattributes の2行は、Gson などがジェネリクスやアノテーションを参照する場合に必要になります。

enum を文字列から復元している箇所があるなら、values()valueOf() を残しておくと安全です。

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

ネイティブ側やライブラリから名前で呼ばれるブリッジクラスがあれば、それも個別に keep します。

-keep class com.yourapp.NativeBridge { *; }

ルールを書いたら npx expo prebuild --cleanandroid/ を作り直し、リリースビルドを通します。私の場合は、モデルのパッケージを keep した時点で当該画面のクラッシュは止まりました。個人開発で複数のアプリを配信していると、こうした「本番だけ」の不具合に一人で向き合う場面が時々あります。圧縮そのものを諦めずに済んだのは収穫でした。

同じ事故を繰り返さないための備え

私自身、R8 を有効にした直後は、必ず実機でリリースビルドを一周触るようにしています。配信用と同じ AAB を --local でビルドして手元の端末に入れ、主要画面を順に開くだけで、剥がれ由来のクラッシュはほぼ事前に見つかります。

mapping.txt はビルドごとに変わるので、リリースしたバージョンの分を必ず保管するか、Play Console にアップロードしておきます。これがないと、本番のクラッシュを難読化されたまま読むことになり、原因の特定が一段重くなります。

keep ルールは、広く -keep class com.yourapp.** { *; } と一括で当てたくなりますが、それでは圧縮の効果が薄れます。剥がれた範囲だけを最小限で残すほうが、サイズ削減と安定の両立に近づきます。

アプリのサイズ削減そのものについては、Rork アプリのバンドルサイズを減らす実践ガイド に R8 以外の手も整理しています。あわせて読んでいただけると、削るところと残すところの線引きがしやすくなるはずです。

次の一手としては、いま配信中のアプリで enableProguardInReleaseBuilds を有効にしているなら、手元で --local のリリースビルドを一本作って主要画面を触ってみてください。剥がれが潜んでいれば、ユーザーより先に気づけます。同じ課題に取り組んでいる方の参考になれば幸いです。

シェア

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

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

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

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

関連記事

開発ツール2026-05-01
Rork で EAS Update を配信したのに反映されない — 原因を切り分ける5つの典型パターン
Rork で EAS Update を publish したのにアプリ側に反映されない、というのは慣れていてもうっかり詰まるトラブルです。よくある5つの原因と、ターミナルで5分以内に切り分ける手順を実例ベースで紹介します。
開発ツール2026-04-29
Rork で作った Android アプリの通知アイコンが白い四角になる問題の直し方
Rork や Expo で作った Android アプリのプッシュ通知アイコンが白い四角や謎のシルエットになる原因と、透過アイコンの正しい作り方・app.json の設定・キャッシュ起因の落とし穴までまとめて解説します。
開発ツール2026-04-25
Rork でアプリアイコンを変更したのに反映されない時の対処法
Rork でアプリアイコンを差し替えたのに古いアイコンが残る — iOS のアイコンキャッシュ、EAS Build のビルドキャッシュ、Android のアダプティブアイコン未設定など、原因別に確実な対処法をまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →