Flutter捕获所有未处理的异常

60

我想在Flutter应用程序中捕获所有未处理的异常,以便将其发送到崩溃报告器。 Flutter文档中有关于如何实现此功能的说明。我按照这些说明,向我的应用程序添加了两个代码段来捕获异常:

通过在runApp中包装runZoned来捕获Dart错误:

runZoned<Future<void>>(
  () async {
    runApp(MyApp());
  },
  onError: (dynamic error, StackTrace stackTrace) {
    print("=================== CAUGHT DART ERROR");
    // Send report
  },
);

通过设置FlutterError.onError来捕获Flutter错误:

FlutterError.onError = (FlutterErrorDetails details) {
  print("=================== CAUGHT FLUTTER ERROR");
  // Send report
};

然而,当我在运行时通过从按钮中抛出异常来测试它时:

throw Exception("Just testing");

控制台中出现以下异常:

════════ Exception Caught By gesture ═══════════════════════════════════════════════════════════════ 在处理手势时抛出了以下异常:Exception: Just testing When the exception was thrown, this was the stack:

... 等等

但我看不到我的打印语句(CAUGHT DART ERROR 或 CAUGHT FLUTTER ERROR),并且在这些行上设置断点似乎从未被触发,因此我认为我的异常处理代码没有捕获它。我错过了什么吗?

这是一个最小的可重复示例(点击按钮,会抛出异常,但并未如预期所捕捉):

import 'dart:async';
import 'package:flutter/material.dart';

void main() =>
  runZoned<Future<void>>(
    () async {
      runApp(MyApp());
    },
    onError: (dynamic error, StackTrace stackTrace) {
      print("=================== CAUGHT DART ERROR");
      // Send report
      // NEVER REACHES HERE - WHY?
    },
  );

class MyApp extends StatefulWidget {
  // This widget is the root of your application.
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  @override
  void initState() {
    super.initState();

    // This captures errors reported by the FLUTTER framework.
    FlutterError.onError = (FlutterErrorDetails details) {
      print("=================== CAUGHT FLUTTER ERROR");
      // Send report
      // NEVER REACHES HERE - WHY?
    };
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: SafeArea(
          child: RaisedButton(
            child: Text("Throw exception"),
            onPressed: () {
              throw Exception("This is a test exception");
            },
          ),
        ),
      ),
    );
  }
}

1
你有没有碰巧能够捕获主函数顶层未处理的异常? - Yadu
4个回答

50

好的,我找到了问题所在。查看了一些相关的Flutter问题:

Flutter工具对异常的捕获过于严格

使热重载在捕获错误时稍微放松一下

当调试器连接时在“未处理”异常处中断

看起来,当处于debug模式时,Flutter框架会捕获许多异常,打印到控制台(有时以红色和黄色的形式在UI中显示),但不会重新抛出——因此它们被有效地吞噬,并且没有办法让你自己的代码捕获它们。但是,当你以release模式部署时,这种情况就不会发生。因此,我的最小可重现示例在release模式下构建时确实可以捕获异常。


37

注意:使用runZonedGuarded更新了样本代码,以避免onError已弃用警告。

@james Allen,我认为可以全局捕获所有未处理的错误并在控制台中处理或打印,而不管模式如何。在你的示例中,我认为你忘记了在设置flutterError.onError之前添加这一行WidgetsFlutterBinding.ensureInitialized(),这样它才能按照你的期望工作。

为了在Flutter中处理未处理异常,我们需要利用以下安全包装来捕获这些异常:

  • Zone(用于捕获所有未处理异步错误
  • FlutterError.onError(用于捕获所有未处理Flutter框架错误

ZONE:

zone并不属于flutter框架,它来自dart本身。在dart文档中它声明如下:

区域保护您的应用程序,使其不会因由异步代码引发的未捕获异常而退出

参考链接: https://dart.dev/articles/archive/zones#handling-asynchronous-errors

因此,通过将我们的flutter应用程序包装在zone内,可以帮助捕获所有未处理的异步错误,下面是简单的示例代码。

示例:

void main() {
  runZonedGuarded(() async {
    runApp(MyApp()); // starting point of app
   },(error, stackTrace) {
      print("Error FROM OUT_SIDE FRAMEWORK ");
      print("--------------------------------");
      print("Error :  $error");
      print("StackTrace :  $stackTrace");
  });
 }

FlutterError.onError :

根据官方的 Flutter 文档,它说:

Flutter 框架会捕获在构建、布局和绘制等自身回调期间发生的错误。

所有这些错误都会被路由到 FlutterError.onError 处理程序。默认情况下,它会调用 FlutterError.dumpErrorToConsole

参考链接: https://flutter.dev/docs/testing/errors

因此,通过使用 flutterError.onError,我们可以捕获所有与 Flutter 框架相关的错误,在下面是一个简单的示例。

示例:

void main() {
    WidgetsFlutterBinding.ensureInitialized(); //imp line need to be added first
    FlutterError.onError = (FlutterErrorDetails details) {
    //this line prints the default flutter gesture caught exception in console
    //FlutterError.dumpErrorToConsole(details);
    print("Error From INSIDE FRAME_WORK");
    print("----------------------");
    print("Error :  ${details.exception}");
    print("StackTrace :  ${details.stack}");
    };
    runApp(MyApp()); // starting point of app
 }

在设置Flutter框架的错误捕获助手之前,不要忘记先添加这一行代码:WidgetsFlutterBinding.ensureInitialized()

注意:

  1. Flutter的红屏死机错误也会在flutterError.onError捕获器下被捕获。
  2. 在开发模式下,默认情况下会看到Flutter红屏死机,而在生产模式下,根据Flutter文档,将只显示普通的背景。
  3. Flutter死机屏幕可以根据我们的创意进行自定义。
  4. 即使这些助手未处理异常,Flutter也不会终止应用程序。
  5. 额外提示 - 还有一个名为catcher的库可供查看错误捕获。

结合Dart和Flutter框架的这两个助手,我们可以全局捕获所有未处理的错误。这是我在Flutter中被指派全局异常处理任务时从Web文档中了解到的内容,如果有任何错误,请随时指正。


嗨,@SaravanaRaja,谢谢你的解释。它帮助我更好地理解了。能否给出一个在runZonedGuarded块中捕获的示例测试错误? - Raghu Mudem
嗨,@SaravanaRaja。我尝试使用runZoned或FlutterError.onError来捕获整个应用程序中的未处理异常,以防止错误类型转换,但它不起作用。我该怎么办?我尝试使用catch(e)并抛出FlutterError或FlutterErrorDetails,但结果相同。 - Quyen Anh Nguyen

10

请确保您按照以下方式使用WidgetsFlutterBinding.ensureInitialized()

runZonedGuarded(() {
  WidgetsFlutterBinding.ensureInitialized(); //<= the key is here
  FlutterError.onError = (FlutterErrorDetails errorDetails) {
    Utilities.log("Will log here ${errorDetails.exception.toString()}");
  };
  runApp(app);
}, (error, stackTrace) {
  Utilities.log("Others catching ${error.toString()}");
});

0

'runZoned'和'Flutter.onError'分别用于Dart和Flutter框架中的'错误'处理。但是在您的代码中,您正在抛出一个'异常'。这不被'run...'或'..onErr'处理。如果您想在控制台中看到"Just testing",请将throw语句修改如下-> throw "Just testing"; 然后您将在控制台日志中看到它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接