Flutter フレームワークの例外キャッチ
Flutter フレームワークは、多くの主要なメソッドの例外をキャプチャします。
レイアウトが範囲外または仕様外になると、Flutter は自動的にエラー インターフェイスをポップアップ表示します。たとえば、
コードにエラーがあっても、APP がクラッシュすることはありません。Flutter は例外をキャッチするのに役立ちます。
Flutter では、Widget は対応する Element を作成し、StatefulWidget は StatefulElement を作成し、StatelessWidget は StatelessElement を作成します。StatefulElement と StatelessElement は両方とも ComponentElement です。
/// Calls the [StatelessWidget.build] method of the [StatelessWidget] object
/// (for stateless widgets) or the [State.build] method of the [State] object
/// (for stateful widgets) and then updates the widget tree.
///
/// Called automatically during [mount] to generate the first build, and by
/// [rebuild] when the element needs updating.
@override
@pragma('vm:notify-debugger-on-exception')
void performRebuild() {
Widget? built;
try {
...
//执行build方法
built = build();
...
debugWidgetBuilderValue(widget, built);
} catch (e, stack) {
_debugDoingBuild = false;
built = ErrorWidget.builder(
_reportException(
ErrorDescription('building $this'),
e,
stack,
informationCollector: () => <DiagnosticsNode>[
if (kDebugMode)
DiagnosticsDebugCreator(DebugCreator(this)),
],
),
);
} finally {
// We delay marking the element as clean until after calling build() so
// that attempts to markNeedsBuild() during build() will be ignored.
super.performRebuild(); // clears the "dirty" flag
}
try {
_child = updateChild(_child, built, slot);
assert(_child != null);
} catch (e, stack) {
built = ErrorWidget.builder(
_reportException(
ErrorDescription('building $this'),
e,
stack,
informationCollector: () => <DiagnosticsNode>[
if (kDebugMode)
DiagnosticsDebugCreator(DebugCreator(this)),
],
),
);
_child = updateChild(null, built, slot);
}
}
ComponentElement-performRebuild メソッドは、StatelessWidget または State の build メソッドを呼び出し、Widget ツリーを更新するために使用されます。
try-catch を使用して例外をキャッチします。例外がキャッチされたら、ErrorWidget ポップアップ プロンプトを作成します。
ErrorWidget.builder は、例外表示のために _debugReportException メソッドによって返される FlutterErrorDetails を受け入れます。
FlutterErrorDetails _reportException(
DiagnosticsNode context,
Object exception,
StackTrace? stack, {
InformationCollector? informationCollector,
}) {
//构建异常详情对象
final FlutterErrorDetails details = FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'widgets library',
context: context,
informationCollector: informationCollector,
);
//报告异常
FlutterError.reportError(details);
return details;
}
FlutterError.reportError メソッドを通じて例外を報告する
static void reportError(FlutterErrorDetails details) {
onError?.call(details); //调用了onError回调
}
onError は FlutterError の静的プロパティであり、デフォルトの処理メソッド dumpErrorToConsole があります。
自分で例外を報告したい場合は、カスタムのエラー処理コールバックを提供するだけで済みます。
FlutterError.onError = (FlutterErrorDetails details) {
reportError(details);
};
その他の例外キャッチ
同期例外キャッチ
同期例外は try/catch を通じてキャッチできます。
// 使用 try-catch 捕获同步异常
try {
throw StateError("xxx");
} catch (e, stack) {
print(e);
}
非同期例外キャッチ
非同期例外は try/catch ではキャッチできません
try {
Future.delayed(Duration(seconds: 1)).then((e) => Future.error("xxx"));
} catch (e) {
print(e)
}
非同期例外をキャッチするには、Future が提供する catchError ステートメントを使用します。
// 使用 catchError 捕获异步异常
Future.delayed(Duration(seconds: 1))
.then((e) => Future.error("xxx"))
.catchError((e)=>print(e));
同期例外と非同期例外の両方を監視する方法はありますか?
ゾーンとプラットフォームディスパッチャー
ゾーンはコード実行の環境範囲を表します。異なるゾーンのコード コンテキストは異なり、相互に影響しません。コード実行サンドボックスと同様に、さまざまなサンドボックスが分離されています。サンドボックスは一部のコード動作をキャプチャ、インターセプト、または変更できます。たとえば、ゾーンはログ出力、タイマー作成、マイクロタスクのスケジュール動作をキャプチャできます。ゾーンは、未処理の例外をすべてキャプチャすることもできます。
runZonedGuarded(() => runApp(MyApp()), (error, stack) {
print(error);
});
これは公式には推奨されなくなりました。PlatformDispatcher の方が推奨されます。
PlatformDispatcher.instance.onError = (error, stack) {
print(error);
return true;
};
例外をキャッチして報告する
void main() {
var onError = FlutterError.onError; //先将 onerror 保存起来
FlutterError.onError = (FlutterErrorDetails details) {
onError?.call(details); //调用默认的onError处理
reportError(details);
};
PlatformDispatcher.instance.onError = (error, stack) {
reportError(error);
return true;
};
runApp(const MyApp());
}
参考ドキュメント:
https://flutter.cn/docs/testing/errors