Sample code for this article

Dart exception handling and Dart’s single-threaded model should be understood before Flutter exception capture. Only when we know the code execution flow can we know where to catch the exception

Exceptions in Dart

Dart can throw and catch exceptions, or if none are caught, they will be thrown, ultimately causing the program to terminate

Unlike Java, all exceptions in Dart are not checked for exceptions. Methods do not declare the exceptions they throw, nor are they required to catch any

Dart provides the Exception and Error types, as well as several subtypes. You can also customize the exception type. In addition, the Dart program can throw any non-NULL object, not just Exception and Error objects.

  • throw

    throw FormatException('Expected at least 1 section');
    Copy the code
  • Throw an arbitrary object

    throw 'Out of llamas! ';
    Copy the code
  • Throw an exception where the expression is used

    void distanceTo(Point other) => throw UnimplementedError();
    Copy the code
  • Catch

    Catch exceptions

    try {
      breedMoreLlamas();
    } on OutOfLlamasException {
      // a special exception
      buyMoreLlamas();
    } on Exception catch (e) {
      // Any other exceptions
      print('Unknown exception: $e');
    } catch (e) {
      // No specified type, handle all exceptions
      print('Something really unknown: $e');
    }
    Copy the code

    The catch function can specify 1 or 2 parameters, the first being the exception object and the second being the StackTrace object.

    try {
      / /...
    } on Exception catch (e) {
      print('Exception details:\n $e');
    } catch (e, s) {
      print('Exception details:\n $e');
      print('Stack trace:\n $s');
    }
    Copy the code

    If some exceptions need to be handled, you can use rethrow to rethrow the exception

    void misbehave() {
      try {
        dynamic foo = true;
        print(foo++); // Runtime error
      } catch (e) {
        print('misbehave() partially handled ${e.runtimeType}. ');
        rethrow; // Allow callers to see the exception.}}void main() {
      try {
        misbehave();
      } catch (e) {
        print('main() finished handling ${e.runtimeType}. '); }}Copy the code
  • finally

    Finally is executed whether or not an exception is tried. If a try holds an exception, the corresponding catch is executed first, and finally

Dart single-threaded model

If an exception is sent in an application and not caught, the application will be terminated, but this is not the case in Dart, because of its execution mechanism. Such as the programming language is Java multi-thread model, an arbitrary thread trigger the exception and exception when not captured, will lead to the whole process, but the Dart will not, because the Dart is the single thread model, operating mechanism is very similar, but there are still some differences, according to the picture below to see roughly (translated from the official figure) :

Dart runs as a message loop in a single-threaded system, consisting of two task queues: microTask Queue and Event Queue. As you can see from the figure, the microtask queue is higher than the event queue

After the Dart thread is executed, the message loop starts. Tasks in the microtask queue are executed one by one in first-in, first-out order. After the event is executed, the program exits. However, new microtasks and event tasks can also be inserted during the execution of event tasks. In this case, the execution process of the whole county is always in a loop and never exits, while in Flutter, the execution process of the main thread is just like this and never stops

In Dart, all external event tasks are in the event queue, such as IO, timer, click, and draw events. And micro task is usually based on the Dart, and micro tasks is very little, so, because the higher priority task queue, if the task too much, the execution time is longer, the longer time the queue delay is, the most intuitive feel for the UI is card, so you must guarantee the micro task team will not be too long. We can add a task to the microtask queue using the future.microTask () method

Flutter anomaly capture

Flutter frame anomaly capture

The Flutter framework provides us with an exception replenishment in many places. For example, when a layout is out of bounds or irregular, Flutter automatically pops up an error page.

This is because Flutter has added exception catching to the build method.

@override
void performRebuild() {
........
  try {
    built = build();
  } catch (e, stack) {
    _debugDoingBuild = false;
    built = ErrorWidget.builder(
      _debugReportException(
        ErrorDescription('building $this'),
        e,
        stack,
        informationCollector: () sync* {
          yield 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.
    _dirty = false;
    assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(false)); }... }Copy the code

We can see that the Flutter handles an ErrorWidget when an exception occurs. What should we do if we want to capture the exception and report it to the alarm platform?

Let’s go to the _debugReportException() method:

FlutterErrorDetails _debugReportException(
  DiagnosticsNode context,
  Object exception,
  StackTrace? stack, {
  InformationCollector? informationCollector,
}) {
  // Build the error object
  final FlutterErrorDetails details = FlutterErrorDetails(
    exception: exception,
    stack: stack,
    library: 'widgets library',
    context: context,
    informationCollector: informationCollector,
  );
  // Report an error
  FlutterError.reportError(details);
  return details;
}
Copy the code

Continue with fluttererror.reporterror ()

/// Calls [onError] with the given details, unless it is null.
static void reportError(FlutterErrorDetails details) {
  assert(details ! =null);
  assert(details.exception ! =null);
  if(onError ! =null) {
    onError!(details);
  }
}
Copy the code

As you can see onError is a static property, it has a default handling method, dumpErrorToConsole, defined in assertions. Dart, which you can find by tracing onError

As you can see from the above code, you can call reportError() directly to report your caught exception, which is itself a static method, as shown below:

class ExceptionTest extends StatelessWidget {
  final error =
      (FlutterErrorDetails detail) => FlutterError.reportError(detail);

  @override
  Widget build(BuildContext context) {
    try {
      throw NullThrownError();
    } catch (e, s) {
      var details = FlutterErrorDetails(
        exception: e,
        stack: s,
        library: "lib.exception",); error(details); }// TODO: implement build
    throwUnimplementedError(); }}Copy the code

Eventually, the exception you throw is reported to the system and pops into the ErrorWidget with stack information printed on the console:

runZoned()

Dart has a runZoned() method that assigns a Zone to an object. A Zone indicates the scope of a code execution environment. A sandbox can capture, block, or modify code behavior such as logging output, Timer creation, and microtask calls in a Zone. A Zone can also capture all unhandled exceptions.

R runZoned<R>(R body(),
    {Map<Object?.Object?>? zoneValues,
    ZoneSpecification? zoneSpecification,
    Function? onError}) {
}
Copy the code
  • ZoneValues: Private data of a Zone, which can be obtained by Zone [key]. It can be regarded as private data of each sandbox

  • ZoneSpecification: Indicates the configurations of a Zone. You can customize certain behaviors, such as the behavior of intercepting logs.

    runZoned(() {
      print('hello world');
    }, zoneSpecification: new ZoneSpecification(
      print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
        parent.print(zone, "Intercepted $line"); }));Copy the code

    I/flutter (4182): Intercepted hello world; If parent. Print is not called, it will not print

    In this way, all calls to the print method in our app to enter the log will be intercepted. In this way, we can also log in the app, and when the app fires an uncaught exception, we will report this and the log

  • onError

    Zone is not captured to handle the callback. If the developer provides the onError callback or through ZoneSpecification. HandleUncaughtError specifies the error correction, then this zone will become an error – zone, An uncaught exception (whether synchronous or asynchronous) in this error-zone is invoked with developer-provided callbacks such as:

    runZoned(() {
      print('hello world');
      throw NullThrownError();
    }, zoneSpecification: new ZoneSpecification(
      print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
        parent.print(zone, "Intercepted $line");
      },
    ), onError: (Object obj, StackTrace stack) {
      var details = FlutterErrorDetails(
        exception: obj,
        stack: stack,
        library: "lib.exception",); error(details); });Copy the code

    In this way, combined with the above FlutterError. OnError we can catch all errors in our Flutter application.

    Note that errors within an error-zone do not cross the error-zone boundary. If you want to cross the error-zone boundary to catch exceptions, you can use the common source zone to catch exceptions, for example:

    var future = new Future.value(499);
    runZoned(() {
        var future2 = future.then((_) { throw "error in first error-zone"; });
        runZoned(() {
            var future3 = future2.catchError((e) { print("Never reached!"); });
        }, onError: (e) { print("unused error handler"); });
    }, onError: (e) { print("catches error of first error-zone."); });
    Copy the code

Finally, our code for exception catching looks like this

void main() {
  runZoned(() => runApp(MyApp()), zoneSpecification: ZoneSpecification(
      print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
    collectLog(line); // Collect logs
  }), onError: (Object obj, StackTrace stack) {
    makeDetails(obj, stack); // Create an error message and report it to the platform
  });
}
Copy the code

Reference to the practical use of Flutter (book)