1. Flutter 异常概要#
Flutter の例外の種類とキャッチに関する記事は、すでに多くのものがオンラインで存在していますので、ここでは詳細には触れません。ここでは、記事の完全性を保証するために、簡単なまとめを行います。
Flutter の例外は、次のカテゴリに分けることができます:
- Dart の例外
- アプリの例外
- 同期例外
- 非同期例外
- フレームワークの例外
- アプリの例外
- エンジンの例外
Dart の例外は、ソースによってアプリの例外とフレームワークの例外に細分化されます。アプリの例外は、例外コードの実行順序に基づいて、同期例外と非同期例外に分類されます:
- 同期例外は、try-catch 構文を使用してキャッチできます。
- 非同期例外は、Future の catchError ステートメントを使用してキャッチする必要があります。
また、Flutter では Zone.runZoned メソッドが提供されており、Dart では Zone はコードの実行範囲を表すもので、サンドボックスのようなものです。Zone が提供する onError コールバック関数を使用して、キャッチされていないすべての例外をインターセプトすることができます。同期例外も非同期例外もインターセプトできるため、通常は runApp レベルですべてのアプリの例外をキャッチします。
runZoned<Future<Null>>(() async {
runApp(MyApp());
}, onError: (error, stackTrace) async {
//エラーの処理
});
フレームワークの例外は、通常はWidget のビルド時にスローされるもので、デフォルトの ErrorWidget は開発時のエラー画面です。これはオーバーライドすることもできます。
ビジネス上、Flutter フレームワークがスローした例外をインターセプトするために、FlutterError.onError
のコールバックを登録することができます。
FlutterError.onError = (FlutterErrorDetails details) {
reportError(details.exception, details.stack);
};
Flutter エンジンの例外は、Android の場合は libflutter.so で発生し、iOS の場合は Flutter.framework で発生します。この種のエラーは、プラットフォーム側のクラッシュ収集 SDK(例:Firebase Crashlytics、Bugly など)に処理を任せます。詳細は後ほど説明します。
2. グレーディング戦略#
オンラインビジネスの安定性と一部の運用要件に対応するために、オンラインの Flutter ビジネスには包括的なグレーディング戦略とダウングレードの計画が必要です。まず、このセクションではグレーディング戦略について説明します。
グレーディングのロジックフローは次のようになります:グレーディング戦略の設定 - バックエンドからの設定の配信とクライアントの設定のロード - クライアントの設定の処理。
2.1 グレーディング戦略の設定#
私たちは内部の設定プラットフォームで、Flutter のグレーディングに必要ないくつかの設定フィールドを定義しています。具体的には:
key
:対応する Flutter ページ(route)appkey
:この設定に対応するホストアプリminVersion
:最小有効バージョンmaxVersion
:最大有効バージョンtype
:グレーディング戦略。具体的には、末尾グレーディング、地域グレーディング、デバイス無効化、システム無効化、ハイブリッドモード、ホワイトリストモードなどがあります。ホワイトリストモードはテストのために提供され、ハイブリッドモードはさまざまな戦略を構成して有効にすることができます。action
:有効範囲、全体的に有効、全体的に無効、グレーディングによる有効など。url
:ダウングレードのリンクで、パラメータの置換文字をサポートしています。クライアントは Flutter の route のパラメータを url のクエリパラメータに連結することができます。
2.2 バックエンドからの設定の配信とクライアントの設定のロード#
コールドスタートとホットスタートの両方で設定を取得します。失敗時には 3 回のリトライがありますので、ローカルにシングルトンを保持し、ビジネス側で Flutter ページを開くたびにグレーディングの設定をチェックして、Flutter ページを開くかどうかを決定します。もちろん、3 回のリトライがすべて失敗することを防ぐために、リリース時には各 Flutter ページのダウングレード設定マップをローカルに保存し、極端な場合には自動的にダウングレードを開始します。
2.3 クライアントの設定の処理#
ビジネス側で Flutter ページを開くときは、グレーディングの設定をチェックして、Flutter ページを開くかどうかを決定します。グレーディングに該当しない場合、つまりダウングレードが発生した場合は、ダウングレードのリンクを取得し、URL パラメータを設定した後、WebView でダウングレード後の H5 を開きます。
注意点として、私たちのビジネスは基本的には H5 から Flutter に移行していますので、デフォルトでダウングレードバージョンがあり、信頼性も保証されています。将来的には、Flutter のみを使用する新しいビジネスについても、Flutter Web のユニバーサルソリューションを予備調査しています。
3. ダウングレードの計画#
Flutter ビジネスの信頼性を確保するために、タイムリーなダウングレードが必要です。グレーディングとダウングレードは、基本的にはビジネスが Flutter または H5 を使用するかどうかを区別するためのものであり、前者は手動で設定され、後者は自動的に有効になります。ローカルでは、アプリのバージョンに基づいてダウングレードの設定を管理し、ページを開く前にダウングレードが必要かどうかをチェックします。以下のシナリオでは、タイムリーなダウングレードが必要です:
3.1 グレーディングのダウングレードが該当しない場合#
前述のように、ビジネス側でグレーディング戦略の設定が行われている場合、該当するグレーディングのダウングレードがない場合に対応する Flutter ページを開く場合、そのページはダウングレードされ、報告される必要があります。
3.2 フレームワークの例外によるダウングレード#
Flutter フレームワークの例外がキャッチされた場合、そのページを「ダウングレードが必要」としてマークし、カスタムの ErrorWidget を提供してユーザーにページのエラーを再入力するように促します。その後、ユーザーが次回そのページにアクセスすると、ダウングレードがトリガーされ、H5 ページにリダイレクトされます。
Dart の例外については、Dart はイベントループメカニズムを使用してタスクを実行するため、各タスクの実行状態は互いに独立しています。つまり、例外が発生すると、現在のタスクの後続のコードは実行されませんが、ユーザーはページの他の機能を引き続き使用することができます。影響範囲は限定的であり、強制的なダウングレード処理は行われません。ただし、エラーは報告されます。
3.3 エンジンのクラッシュによるダウングレード#
ただし、エンジンにエラーが発生すると、アプリがクラッシュする可能性があります。この場合、ログを報告するだけでなく、フラグを設定し、次回アプリを開いたときに Flutter エンジンを起動せず、すべての Flutter ページをダウングレードします。
3.4 プロダクトのロードに失敗した場合のダウングレード#
技術的には、カスタムエンジンを使用して Flutter のプロダクトをトリミングしています。各リリース時には、App.framework に対応するトリミングされたパッケージの MD5 ハッシュが保存されます。ユーザーがアプリを初めて起動すると、トリミングされたパッケージをダウンロードしてエンジンを起動し、パッケージの整合性を確認した後、カスタムエンジンでトリミングされたパッケージをロードします。ただし、パッケージのダウンロードに失敗する場合があります。段階的なリトライ以外にも、この場合は Flutter エンジンを起動せず、すべてのページをダウングレードして報告します。
3.5 Flutter 関連のクラッシュによるダウングレード#
また、Flutter に関連するクラッシュもあります。これはエンジンのクラッシュでもなく、プロダクトのロードの問題でもなく、Flutter の例外でもありません。これは、プラグインのネイティブ側の実装のロジックの問題によるものです。このような場合も、Flutter 関連のクラッシュに該当しますが、Bugly のログには Flutter という言葉は見つかりません。なぜなら、プログラムが終了するときには、Flutter の内部やエンジン側ではないからです。
このような場合、クラッシュまたは ANR の報告時の topViewController を記録し、トレースパスを追跡します。現在のルートスタックに Flutter のアクティビティまたは FlutterViewController が存在する場合は、念のためにクラッシュをダウングレードします。
4. オペレーションデイリーレポート#
Flutter のオペレーションデイリーレポートのデータソースは、パフォーマンスレポートと例外レポートです。クラッシュの監視とアラートについては、クライアントの Bugly に任せています。デイリーレポートでは、さまざまな App バージョンでの各 Flutter ページのパフォーマンスに関する情報が記録されており、読者に以下の指標を提供しています:
- PV
- アクセス成功率
- クラッシュ率、クラッシュの影響を受けるユーザー数
- 300ms の閾値を超える秒開率
- ダウングレード率、グレーディング率
- ...
最後に、プロダクトのダイナミックロードとダウングレード戦略を組み合わせた起動フローチャートは以下の通りです: