Flutter异常指的是Flutter程序在运行时所抛出的异常分为:
- Dart代码运行时发生的异常
- Flutter框架异常
- 原生代码运行时抛出的异常,如:Android 的Java和kotlin,iOS的OC和swift
做Flutter应用Dart代码占绝大多数,所以本文我们重点学习下Flutter中Dart和框架异常的捕获与收集。
Dart代码运行时发生的异常与Java、kotlin、OC等具有多线程模型的编程语言不同,Dart是一门单线程的编程语言,采用事件循环机制来运行任务,所以各个任务的运行状态是互相独立的。也即是说,当程序运行过程中出现异常时,即使没有像Java那样使用try-catch机制来捕获异常,Dart程序也不会退出,只会导致当前任务后续的代码不会被执行,而其它功能仍然可以继续使用。
异常捕获
根据异常代码的执行时序,Dart异常可以分为同步和异步异常两类。首先我们看同步异常的捕获方式:
同步异常的捕获方式:
//使用try-catch捕获同步异常 try { throw StateError('There is a dart exception.'); } catch (e) { print(e); }
异步异常的捕获有两种方式:
- 一种是使用Future提供的catchError语句来进行捕获;
- 另外一种是将异步转同步然后通过try-catch进行捕获;
//使用catchError捕获异步异常 Future.delayed(Duration(seconds: 1)) .then( (e) => throw StateError('This is first Dart exception in Future.')) .catchError((e) => print(e)); //异步转同步 try { await Future.delayed(Duration(seconds: 1)).then( (e) => throw StateError('This is second Dart exception in Future.')); } catch (e) { print(e); }
集中捕获异常
在Android中我们可以通过Thread.UncaughtExceptionHandler
接口来集中收集异常,那么在Flutter中如何集中收集异常呢?
Flutter提供的Zone.runZonedGuarded()
方法。在Dart语言中,Zone表示一个代码执行的环境范围,其概念类似沙盒,不同沙盒之间是互相隔离的。如果想要处理沙盒中代码执行出现的异常,可以使用沙盒提供的onError回调函数来拦截那些在代码执行过程中未捕获的异常:
runZonedGuarded(() {
throw StateError('runZonedGuarded:This is a Dart exception.');
}, (e, s) => print(e));
runZonedGuarded(() {
Future.delayed(Duration(seconds: 1)).then((e) => throw StateError(
'runZonedGuarded:sThis is a Dart exception in Future.'));
}, (e, s) => print(e));
从上述代码中不难看出,无论是同步异常还是异步异常,都可以使用Zone直接捕获到。同时,如果需要集中捕获Flutter应用中未处理的异常,那么可以把main函数中的runApp语句也放置在Zone中,这样就可以在检测到代码运行异常时对捕获的异常信息进行统一处理:
runZonedGuarded<Future<Null>>(() async {
runApp(BiliApp());
}, (e, s) => print(e));
最终DEMO
void main() {
AppCatchError().run(MineApp());
}
//全局异常的捕捉
class AppCatchError {
run(Widget app) {
///Flutter 框架异常
FlutterError.onError = (FlutterErrorDetails details) async {
///线上环境
///TODO
if (kReleaseMode) {
Zone.current.handleUncaughtError(details.exception, details.stack!);
} else {
//开发期间 print
FlutterError.dumpErrorToConsole(details);
}
};
runZonedGuarded(() {
//受保护的代码块
runApp(app);
}, (error, stack) => catchError(error, stack));
}
///对搜集的 异常进行处理 上报等等
catchError(Object error, StackTrace stack) {
print("AppCatchError>>>>>>>>>>: $kReleaseMode"); //是否是 Release版本
print('AppCatchError message:$error,stack$stack');
}
}
异常上报
捕获到异常后可以在上述_reportError
方法中上报到服务端,像BAT等一线互联网大厂都有自己的Crash监控平台。如果公司没有自己的Crash平台,可以接入第三方的如:Buggly。