1. 背景
アプリケーションのパフォーマンスの安定性は、優れたユーザー エクスペリエンスにとって非常に重要な部分であり、アプリケーションのパフォーマンスの安定性をより確実にするために、例外キャプチャはオンライン製品の安定性を確保する上で重要な役割を果たします。私たちのチームがU-APM モバイル アプリケーション パフォーマンス監視製品を発表してから、開発者が多くのオンライン問題を特定して解決するのに役立ちました。ユーザー数の増加と注目の高まりに伴い、訪問した顧客や開発者のメッセージでは、多くの開発者がこの製品がフラッター フレームワークの例外キャプチャをサポートできることを期待しています。私自身フラッター開発をしたことがないので、主に既存製品の機能に基づいたプラグインによる例外報告を行っていますが、この記事ではフラッターエラー処理を学ぶ過程と遭遇した問題を記録します。
2. フラッター例外
Flutter 例外とは、Flutter プログラムの Dart コードの実行中に予期せず発生するエラー イベントを指します。
3. Flutterの異常特性
Dart は単一プロセス メカニズムであるため、このプロセスで問題が発生した場合、現在のプロセスにのみ影響します。Dart はイベント ループ メカニズムを使用してタスクを実行します。タスクで例外が発生し、それがキャッチされなかった場合、プログラムは終了ではなく直接その結果、現在のタスクの後続のコードは実行されません。これは、1 つのタスクの例外が他のタスクの実行に影響を与えず、各タスクの実行ステータスが互いに独立していることを意味します。 。
たとえば、Java に似た try-catch メカニズムを通じてそれをキャッチできます。ただし、Java とは異なり、Dart プログラムでは例外の処理が強制されません。
4. フラッター例外の分類
Flutterの開発において、例外は例外の発生源によってフレームワーク例外とアプリ例外に分けられます。Flutter は、これら 2 種類の例外に対して異なるキャプチャ方法を提供します。フレームワーク例外は、Flutter フレームワークによって発生する例外であり、通常は、Flutter フレームワークの下部で異常な判断を引き起こす不正なアプリケーション コードによって引き起こされます。アプリ例外の場合、これはアプリケーション コードの例外であり、通常、処理されなかったアプリケーション層の他のモジュールによってスローされた例外によって引き起こされます。アプリの例外は、例外コードの実行タイミングにより、同期例外と非同期例外の 2 つに分類されます。
5. 捕獲方法
1. アプリ例外の捕捉方法
同期例外のキャッチには、try-catch メカニズムが使用されます。
//
使用 try-catch 捕获同步异常
try {
throw StateError('This is a Dart exception.');
}
catch(e) {
print(e);
}
非同期例外をキャッチするには、Future が提供する catchError ステートメントを使用します。
//
使用 catchError 捕获异步异常
Future.delayed(Duration(seconds: 1))
.then((e) => throw StateError('This is a Dart exception in Future.'))
.catchError((e)=>print(e));
これを見て、同期例外と非同期例外の両方を監視する方法はないだろうかと心の中で疑問に思う人も多いと予想されます。
答えは「はい」です。
Flutter は、コード内のすべての例外を管理する Zone.runZoned メソッドを提供します。コード実行オブジェクトのゾーンを指定できます。Dart では、ゾーンはコード実行の環境範囲を表します。その概念はサンドボックスに似ており、異なるサンドボックスは互いに分離されています。サンドボックス内のコード実行の例外を観察したい場合、サンドボックスは、コード実行オブジェクト内のキャッチされなかった例外をインターセプトする onError コールバック関数を提供します。これ以上ナンセンスなことはありません。
コードを見せてください!
runZoned(() {
// 同步异常
throw StateError('This is a Dart exception.');
}, onError: (dynamic e, StackTrace stack) {
print('Sync error caught by zone');
});
runZoned(() {
// 异步异常
Future.delayed(Duration(seconds: 1))
.then((e) => throw StateError('This is a Dart exception in Future.'));
}, onError: (dynamic e, StackTrace stack) {
print('Async error aught by zone');
});
Flutter アプリケーションで未処理の例外を一元的にキャプチャできるようにするために、最終的に runApp ステートメントをゾーンの main 関数に配置しました。このようにして、コード内で例外が検出された場合、取得した例外コンテキスト情報に従って均一に処理できます。
runZoned>(() async {
runApp(MyApp());
}, onError: (error, stackTrace) async {
//Do sth for error
});
2. フレームワーク例外の捕捉方法
Flutter フレームワークは、多くの主要なメソッドで例外キャプチャを提供します。自分で例外を報告したい場合は、次のようなカスタム エラー処理コールバックを提供するだけです。
void main() {
FlutterError.onError = (FlutterErrorDetails details) {
reportError(details);
};
...
}
上記の例外を均一に処理できる一連のコードが空から降ってきたのでしょうか?
3. 概要 (すべての例外をキャッチするためのコードのセット)
runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
FlutterError.onError = (FlutterErrorDetails details) {
myErrorsHandler.onError(details.exception,details.stack);
};
runApp(MyApp());
}, (Object error, StackTrace stack) {
myErrorsHandler.onError(error, stack);
});
コード内に一文があり、アピールに一度も登場していないコードは WidgetsFlutterBinding.ensureInitialized() です。このコード行をコメントアウトすると、フレームワーク例外をキャッチできません。
長い間悩んでいたのですが、やっと理由が分かりました。
上の図は Flutter のアーキテクチャ層であり、Flutter エンジンとの対話には WidgetFlutterBinding が使用されます。APM 製品は初期化のためにネイティブ コードを呼び出す必要があり、プラグインはプラットフォーム チャネルを使用してネイティブ コードを呼び出す必要があり、これは非同期で行われるため、ensureInitialized() を呼び出して WidgetsBinding のインスタンスがあることを確認する必要があります。
ドキュメントから :
WidgetsBinding のインスタンスを返し、必要に応じて作成および初期化します。作成された場合は、WidgetsFlutterBinding になります。以前に初期化されていた場合は、少なくとも WidgetsBinding が実装されます。
注: アプリケーションが runApp の WidgetsFlutterBinding.ensureInitialized() メソッドを呼び出して初期化操作を実行する場合は、runZonedGuarded の WidgetsFlutterBinding.ensureInitialized() を呼び出す必要があります。
6. 異常報告
例外レポートの全体的なソリューションは、既存のプラグインを通じてインターフェイスを追加し、Android APM ライブラリと iOS APM ライブラリのカスタム例外レポート インターフェイスをブリッジすることです。
プラグイン追加機能
static void postException(error, stack) {
List args = [error,stack];
//将异常和堆栈上报至umapm
_channel.invokeMethod("postException",args);
}
Android 側はカスタム例外レポートを呼び出します。
iOS 側でカスタム例外レポートを呼び出します。
if ([@"postException" isEqualToString:call.method]){
NSString* error = arguments[0];
NSString* stack = arguments[1];
[UMCrash reportExceptionWithName:@"Flutter" reason:error stackTrace:stack terminateProgram:NO];
}
以上が今回の内容のご紹介でございますが、弊社の技術内容が開発者の皆様の課題解決に少しでもお役に立てれば幸いであり、私たちは開発者の進歩に寄り添い、共に成長してまいります。
オリジナル技術演習フェーズ 2|Flutter Exception Capture-Alibaba Cloud Developer Community