Code前端首页关于Code前端联系我们

Flutter应用开发:捕捉Bug的正确位置

terry 2年前 (2023-09-23) 阅读数 80 #移动小程序

在软件开发过程中,错误和异常总是不可避免的。

无论是客户端的逻辑错误,还是服务端的数据问题,只要出现异常,我们就需要一种机制来通知我们以便处理。

在应用开发过程中,我们可能会通过一些第三方平台如Fabric、Bugly等开启异常日志上报。

Flutter也有一些第三方平台,比如Sentry,可以实现异常日志上报。

但更笼统地说,本文并没有具体解释第三方平台上的异常日志捕获。我们将告诉你如何在 Flutter 中保存异常。

至于具体的上报方式,无论是向自己的后台上报,还是通过第三方SDK API上报异常,都是可以的。

演示开始状态

首先我们新建一个Flutter项目,编辑main.dart代码如下:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Flutter Crash Capture'),),
        body: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}
复制代码

效果如下: Flutter app开发:错误捕获的正确姿势

捕获错误

编辑MyHome列表页面。并进行越界访问,更改一些代码如下:

class MyHomePage extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   List<String> numList = ['1', '2'];
   print(numList[6]);
   return Container();
 }
}
复制代码

可以看到控制台错误如下:

flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following RangeError was thrown building MyHomePage(dirty):
flutter: RangeError (index): Invalid value: Not in range 0..1, inclusive: 6
复制代码

当然,这些错误信息也出现在用户界面(调试模式)中。

那么我们如何捕捉它呢?

其实很简单。有一个一般模式。模型为:

import 'dart:async';

import 'package:flutter/material.dart';

Future<Null> main() async {
  FlutterError.onError = (FlutterErrorDetails details) async {
    Zone.current.handleUncaughtError(details.exception, details.stack);
  };

  runZoned<Future<void>>(() async {
    runApp(MyApp());
  },  onError: (error, stackTrace) async {
    await _reportError(error, stackTrace);
  });
}

Future<Null> _reportError(dynamic error, dynamic stackTrace) async {
  // TODO
}
复制代码

TODO 中,您可以执行埋点的报告功能或其他处理。

完整的例子如下:

import 'dart:async';

import 'package:flutter/material.dart';

Future<Null> main() async {
  FlutterError.onError = (FlutterErrorDetails details) async {
    Zone.current.handleUncaughtError(details.exception, details.stack);
  };

  runZoned<Future<void>>(() async {
    runApp(MyApp());
  },  onError: (error, stackTrace) async {
    await _reportError(error, stackTrace);
  });
}

Future<Null> _reportError(dynamic error, dynamic stackTrace) async {
  print('catch error='+error);
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Flutter Crash Capture'),),
        body: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    List<String> numList = ['1', '2'];
    print(numList[6]);
    return Container();
  }
}
复制代码

运行时会看到控制台检测到的错误是:

flutter: catch error=RangeError (index): Invalid value: Not in range 0..1, inclusive: 6
复制代码

assert Magical Use

我们知道通常只有打包后才需要报错并释放。

如果您在正常调试过程中遇到错误,我们会定位问题并修复。

所以在调试模式下我们不想报告错误,我们想将它们直接打印到控制台。

所以现在我们需要一种方法来区分调试模式和发布模式。如何区分?

此时必须使用验证。

bool get isInDebugMode {
  // Assume you're in production mode.
  bool inDebugMode = false;

  // Assert expressions are only evaluated during development. They are ignored
  // in production. Therefore, this code only sets `inDebugMode` to true
  // in a development environment.
  assert(inDebugMode = true);

  return inDebugMode;
}
复制代码

从注释中还可以看出,断言子句仅在开发环境中有效,在生产环境中被忽略。

所以利用这个我们就可以实现我们的需求了。

上面的结论很容易验证,我们就不介绍了。

完整模型

import 'dart:async';

import 'package:flutter/material.dart';

Future<Null> main() async {
  FlutterError.onError = (FlutterErrorDetails details) async {
    if (isInDebugMode) {
      FlutterError.dumpErrorToConsole(details);
    } else {
      Zone.current.handleUncaughtError(details.exception, details.stack);
    }
  };

  runZoned<Future<void>>(() async {
    runApp(MyApp());
  },  onError: (error, stackTrace) async {
    await _reportError(error, stackTrace);
  });
}

Future<Null> _reportError(dynamic error, dynamic stackTrace) async {
  // TODO
}

bool get isInDebugMode {
  // Assume you're in production mode.
  bool inDebugMode = false;

  // Assert expressions are only evaluated during development. They are ignored
  // in production. Therefore, this code only sets `inDebugMode` to true
  // in a development environment.
  assert(inDebugMode = true);

  return inDebugMode;
}
复制代码

调试模式下,错误会直接打印到控制台,帮助定位问题。

发布模式下,错误信息会被收集并上传到服务器。

作者:Android小宇
来源:掘金

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门