お疲れ様です。すぺきよです。
最近仕事でFlutterを扱うことが増えてきました。
自分でも開発して、メンバーにも開発してもらって、テストも終われば次にやることはリリースです。
アプリなので、AndroidはGoogle Play、iOSはApp Storeにリリース作業をするわけです。
今回はAndroidにリリースするときに、表題のエラーで苦しめられたので、その備忘録です。
何があったの?
ことの発端
Flutterで開発したアプリをGoogle Play Consoleでリリースしようとしたら、以下の警告でリリースできなくなるよ!と言われました。
Update your Play Core Maven dependency to an Android 14 compatible version!
https://play.google.com/console/
Your current Play Core library is incompatible with targetSdkVersion 34 (Android 14), which introduces a backwards-incompatible change to broadcast receivers to improve user security.
As a reminder, from August 31, Google Play requires all new app releases to target Android 14. Update to the latest Play Core library version dependency to avoid app crashes:
https://developer.android.com/guide/playcore#playcore-migration
いったい何を言っているのさ
警告メッセージの中のURL「https://developer.android.com/guide/playcore#playcore-migration」にアクセスして中を確認すると、Google Play Coreライブラリが4つのライブラリに分割されたそうです。
「今までは分割前のライブラリを使えていたけど、2024年9月1日からはAndroid14(targetSDKVersion 34)への対応が必須になるよ。分割前の古いライブラリは互換性がなくなるので、4つに分割された新しいライブラリを使ってね。じゃないと今後はリリースできないよ」ってことらしいですね。
といわれましても、前任者から引き継いだプロジェクトなので、何を使っていて何を使っていないかわからない状況・・・。
仕方ないので、4つに分割されたライブラリをすべて参照してあげる戦法で逃げることにしました。
どう書き換えたか。
project_root/android/app/build.gradleファイルの末尾に記述されているdependenciesの内容を書き換えました。
変更前
dependencies {
....
implementation 'com.google.android.play:core-ktx:1.8.1'
....
}
変更後
dependencies {
....
implementation 'com.google.android.play:asset-delivery:2.2.2'
implementation 'com.google.android.play:asset-delivery-ktx:2.2.2'
implementation 'com.google.android.play:feature-delivery:2.1.0'
implementation 'com.google.android.play:feature-delivery-ktx:2.1.0'
implementation 'com.google.android.play:review:2.0.1'
implementation 'com.google.android.play:review-ktx:2.0.1'
implementation 'com.google.android.play:app-update:2.1.0'
implementation 'com.google.android.play:app-update-ktx:2.1.0'
....
}
とりあえず動いた
結果、アプリのデバッグビルドが通り、アプリが無事に起動しました。
アプリの機能としてコアな部分をざーっと動かし動作的に問題ないことも確認できました。
問題はリリースビルド
うまく動いたし、修正方針としても異論はないので、これでリリースだ!っとリリースビルドをすると見慣れないエラーで停止・・・。
そのエラーとは以下の内容です。
ERROR: Missing classes detected while running R8. Please add the missing classes or apply additional keep rules that are generated in (path to)/build/app/outputs/mapping/release/missing_rules.txt.
ERROR: R8: Missing class com.google.android.play.core.tasks.OnFailureListener (referenced from: void io.flutter.embedding.engine.deferredcomponents.PlayStoreDeferredComponentManager.installDeferredComponent(int, java.lang.String))
Missing class com.google.android.play.core.tasks.OnSuccessListener (referenced from: void io.flutter.embedding.engine.deferredcomponents.PlayStoreDeferredComponentManager.installDeferredComponent(int, java.lang.String))
Missing class com.google.android.play.core.tasks.Task (referenced from: void io.flutter.embedding.engine.deferredcomponents.PlayStoreDeferredComponentManager.installDeferredComponent(int, java.lang.String) and 1 other context)
FAILURE: Build failed with an exception.
これが赤い文字でばばーっと出るものだから、涙目です。
ここから、調べまくって納得のいく修正にたどり着くまでの半日にわたる調査が始まったわけです。
先に結論の修正方法
Flutterのandroidフォルダ以下のproguard-rules.proファイルの中を以下のように書き換えました。
#Flutter Wrapper
#-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
#-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
具体的には2行目と6行目をコメントアウトしています。
なぜこの修正方法を採用するに至ったか
まずはエラーメッセージをチェック
エラーメッセージの1行目には以下の記述が含まれています
Please add the missing classes or apply additional keep rules that are generated in (path to)/build/app/outputs/mapping/release/missing_rules.txt.
で、このファイルの中身を見ると、以下のように書かれていました。
# Please add these rules to your existing keep rules in order to suppress warnings.
# This is generated automatically by the Android Gradle plugin.
-dontwarn com.google.android.play.core.tasks.OnFailureListener
-dontwarn com.google.android.play.core.tasks.OnSuccessListener
-dontwarn com.google.android.play.core.tasks.Task
これだけ見ても、なんじゃらほいですね。
何か追加しろと言われていますが「dontwarn」の文字列を見るに、警告やエラーを無視する設定であるのは確かです。
ビルドの警告やエラーを無視しても絶対にいいことはないので、この設定はまず不採用なのは確実です。
エラーメッセージの2行目をチェック
エラーメッセージの2行目は以下の文言で始まっています。
ERROR: R8: Missing class com.google.android.play.core.tasks.OnFailureListener
仕方ないのでこのメッセージで検索・・・、最終的に解決にたどり着きます。
Gradleのバージョンを下げてみよ(不採用)
まずは最初に見つかった「https://github.com/flutter/flutter/issues/139462」にアクセスし内容をチェック。Gradleのバージョンを下げよとの指示が見受けられます。
しかし、Gradleはビルド用のスクリプトのようなものです。
Gradleのバージョンを一時的に下げてうまくいったとしても、いつかは上げないとまた「バージョン上げてね」のお達しでリリースできなくなるのは火を見るより明らかなので、これは採用できません。
実際に最近もGradleのバージョンを上げないとリリースできなくなるとというお達しがあったそうですし。
proguard-rules.proをコメントアウトしてみよ(採用)
次に見つかった「https://stackoverflow.com/questions/76800185/how-to-keep-some-com-google-android-play-core-classes-reported-missing-by-r8-d」にアクセス。
さすがはStackOverflow先生。頼りになります。最終的にこちらを採用することとしました。
FlutterがAndroidアプリをビルドをするときに、Proguardさんにお手伝いしてもらうようになっているそうです。
Proguardさんのお仕事はどうやら、そのままビルドしちゃうと、人でもJavaバイトコードから簡単にコードの内容がわかっちゃってよくないから、人には簡単に読めないように難しくする「難読化」をしてくれるそうです。
その際についでに使ってないライブラリとかを探して出力されるパッケージに含まれないように除去してくれるんだそうです。親切ですね。
今回問題になっているのはこの「使ってないライブラリとかを探して出力されるパッケージに含まれないように除去」の親切な部分です。
Proguardさんは、おっちょこちょいな一面があって、使っているのに使ってないと思い込む癖があって、削除しちゃダメな部分まで削除しちゃうそうです。
そうなるとどうなるか。そうビルドが通らなかったりアプリが動かなくなったりするのです。
今回はこれが原因で、「Missing class(クラスが見つからないよ)」と言われていたというのが落ちだったようです。
ちょうど上のほうで書いた「dontwarn」もProguardさんに「これに関するエラーは無視してね」とお願いするコードだったようです。
実際にこの「dontwarn」の設定を採用したらエラーは出なくなりビルドは通りました。でも、エラーを無視するなんて言う設定はありえません。
なぜ、クラスを追加ではなく、既存をコメントアウトしたのか
それであれば、問題となっているクラスを削除しないように設定するのが筋ですね。
ですが、Proguardさんのマニュアルを見て、以下のように設定を「project_root/android/app/proguard-rules.pro」に追加してもうまくいきません。
-keep class com.google.android.play.core.tasks.OnFailureListener
-keep class com.google.android.play.core.tasks.OnSuccessListener
-keep class com.google.android.play.core.tasks.Task
で、参考にしたStackOverflowの記事をよく見ると「io.flutter.app...
,やio.flutter.embedding.engine...
.から呼び出されている特徴がある」と書かれていたので、自分のエラーメッセージも確認してみると・・・。
ERROR: R8: Missing class com.google.android.play.core.tasks.OnFailureListener (referenced from: void io.flutter.embedding.engine.deferredcomponents.PlayStoreDeferredComponentManager.installDeferredComponent(int, java.lang.String))
Missing class com.google.android.play.core.tasks.OnSuccessListener (referenced from: void io.flutter.embedding.engine.deferredcomponents.PlayStoreDeferredComponentManager.installDeferredComponent(int, java.lang.String))
Missing class com.google.android.play.core.tasks.Task (referenced from: void io.flutter.embedding.engine.deferredcomponents.PlayStoreDeferredComponentManager.installDeferredComponent(int, java.lang.String) and 1 other context)
うん、まさにこの方が言われている通りじゃないですか。
というわけで、この方の言っている「project_root/android/app/proguard-rules.pro」への修正である。
#Flutter Wrapper
#-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
#-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
を採用するに至ったという経緯です。
ここまでの調査でも、必要なのに削除されてしまって困るから「-keep class io.flutter.app.** { *; }」の設定があるわけで、本来はこんな設定は必要ないことが正しいはずです。
今回のライブラリ分割によって、これらの設定が必要なくなり、本来あるべき形に収まったと考えればこの修正も納得できます。
実際にこの修正でうまくビルドが通るようになりました。めでたしめでたし。
コメント