Play で公開したアプリが、起動した瞬間に落ちるようになりました。手元の debug ビルドでは何ともありません。原因にたどり着くまでに遠回りしたので、同じ症状の人が検索で辿り着けるように記録を残します。結論から書くと、Android Gradle Plugin 9 系がリリースビルドで R8 をデフォルトで有効にすることが原因でした。
01 — 症状
Dart のコードに到達する前に死ぬ
リリースビルドを実機に入れて起動すると、スプラッシュも出ずに落ちます。
adb logcat を取ると、次の2行が出ていました。
Unable to get provider androidx.startup.InitializationProvider
Failed to create an instance of androidx.work.impl.WorkDatabase
厄介なのは、これが Flutter / Dart のコードが動き始める前に起きている点です。
Dart 側で try/catch をいくら書いても捕まりません。
クラッシュレポートにも Dart のスタックトレースが出ないので、
アプリのコードを疑っているうちは永遠に見つかりません。
02 — 真因
AGP 9 はリリースビルドで R8 が既定で有効
android/settings.gradle.kts で使っていた Android Gradle Plugin のバージョンは 9.0.1 でした。
AGP 9 系は、minifyEnabled を一行も書いていなくてもリリースビルドで R8(コード圧縮・難読化)が走ります。
証拠は分かりやすくて、ビルドすると mapping.txt が勝手に生成されています。
WorkManager と Room は、実行時にリフレクションで WorkDatabase_Impl のような
生成クラスを取りに行きます。R8 から見ると「どこからも呼ばれていないクラス」なので、
削除するか名前を変えてしまいます。結果、起動時の初期化で Room のデータベースが作れずに落ちます。
自分は WorkManager を直接使っていませんでした。引き込んでいたのは
firebase_messaging(FCM)です。FCM を後から追加したので、
「前は起動できたのに、あるときから落ちるようになった」という体感とも一致しました。
debug ビルドで再現しなかったのは、単に debug では R8 が走らないからです。
03 — 修正
まず R8 を明示的に切る
app/android/app/build.gradle.kts の buildTypes.release に、
暗黙の既定値に頼らず明示的に書きました。
buildTypes {
release {
isMinifyEnabled = false
isShrinkResources = false
}
}
これで実機のリリースビルドが起動するようになりました。
APK は 62MB から 69MB に増え、mapping.txt は生成されなくなります。
サイズと引き換えに、まず確実に起動する状態を取り戻した形です。
04 — 教訓
リリース挙動は実機のリリースビルドでしか分からない
| 検証は実機のリリースで | flutter build apk --release → adb install → adb logcat。エミュレータや debug では再現しません。 |
|---|---|
| 暗黙の既定値を疑う | ツールのメジャーバージョンを上げたとき、書いていない設定の既定値が変わっていることがあります。 |
| R8 を戻すなら keep ルールから | 再度有効にするなら、WorkManager・Room・Firebase の keep ルールを過不足なく書き、実機リリースで起動確認してから。漏れると別の箇所で同じ壊れ方をします。 |
このクラッシュはとりまトークで起きたものです。
Follow
こういう記録を置いています
個人開発でつまずいた話や、作りかけのものは Instagram(@kenpiro1012)と note にも置いています。 記事の一覧は記事ページにまとまっています。