概要
Expo(React Native)で作った個人開発アプリをGoogle Play本番にリリースしたところ、アイコンをタップした瞬間にクラッシュする現象が発生しました。スプラッシュも表示されず、毎回落ちます。
結論から言うと、原因はJavaScriptコードではなく、app.json に権限が1行足りていなかったことでした。同じ症状で消耗している人のために、切り分けの過程と原因を残しておきます。
環境
- Expo SDK 54
- react-native-google-mobile-ads(AdMob)
- Android 13+ / 本番リリースビルド(
__DEV__ = false) - 開発ビルドでは再現せず、本番ビルドでのみクラッシュ
症状
- アイコンタップ → スプラッシュを待たずに即クラッシュ
- 100%再現
- 開発時(dev client)では問題なし
「起動直後に落ちる」「本番だけ落ちる」の2点が重要な手がかりでした。
やってしまった回り道
最初、自分のコードが原因だと思い込んでいました(App.jsが約9,000行あり、いかにも怪しかったため)。
- AdMobの初期化を疑い、
.catch()で防御的に囲む → それでもクラッシュ - あるconstが定義より前で参照されている箇所を発見 → だが関数内だったため実際は安全(ミスリード)
- モジュールのトップレベルを
__DEV__ = falseの本番モードでNode上で実行し、react-native / expo などをstubして「ロード時に落ちるか」を検証 → 落ちなかった
つまりJSコードは無罪でした。
本当の原因
Google Mobile Ads SDKは、Android 13 (API 33) 以降で広告IDを扱うために以下の権限が必要です。これが app.json に無いと、SDKがネイティブの初期化段階でクラッシュします。
"android": {
"permissions": [
"com.google.android.gms.permission.AD_ID"
]
}
重要なのは、これはJSが実行される前のネイティブ層で落ちるという点です。そのため、JS側でいくら .catch() を書いても捕捉できません。開発ビルドで再現しなかったのも、本番ビルドでのみ広告SDKがこの経路を通るためと考えられます。
対処
app.json に上記権限を追加し、新しくネイティブビルドを作成します。この修正はネイティブ設定の変更なので、OTAアップデート(eas update)では反映されません。必ず eas build でビルドし直す必要があります。
eas build -p android --profile production
一番速い切り分け方法
コードを眺める前に、実機をUSB接続して以下を実行するのが最短でした。
adb logcat | grep -i "FATAL|AndroidRuntime|ads"
ネイティブクラッシュのスタックトレースがそのまま出るため、原因がAdMobかどうかを即座に確定できます。
まとめ
- 「起動直後に落ちる」は、JSよりネイティブ/設定を疑う
- ネイティブクラッシュはJSの
.catch()では捕捉できない - Android 13+ でAdMobを使うなら
AD_ID権限は必須 - ネイティブ設定の変更はOTAではなく再ビルドが必要
- まず
adb logcatを見るのが最短
同じ現象で「本番だけ起動直後に落ちる」という方は、まず app.json の権限を確認してみてください。