一、Dart 異常捕獲、拋出
在Dart中,異常分兩類:同步異常和異步異常
1、同步異常:
Dart中同步異常可以通過try/on/catch/finally來捕獲代碼塊異常,可以通過throw 關鍵字用來明確地拋出異常。如下案例:
List testList = List(); try { var s = testList[3]; //代碼邏輯 } on IntegerDivisionByZeroException { //使用on關鍵字,捕獲特定類型的異常 } on NoSuchMethodError catch (e) { //使用on關鍵字,捕獲特定類型的異常 //代碼段可以有多個 on / catch 塊來處理多個異常 } catch (error, stacktrace) { // 沒有指定類型,處理所有錯誤 //catch 最多提供兩個可選參數 //第一個參數 error 類型為 Object,也就是異常是可以拋出任意對象。 //第二個參數 stacktrace,表示異常堆棧。 print('Something really unknown error: $error'); print('Something really unknown stacktrace: $stacktrace'); throw '拋出異常關鍵'; //throw 之后的代碼將不會執行 //throw new FormatException(); } finally { // 在throw之前先執行finally代碼塊 print("this is finally"); }
輸出結果:
Dart中的每個異常類型都是內置類 Exception 的子類型。Dart可以通過擴展現有異常來創建自定義異常。定義自定義異常的語法如下所示
語法:定義異常
class Custom_exception_Name implements Exception { // can contain constructors, variables and methods }
eg: class AmtException implements Exception { String errMsg() => 'Amount should be greater than zero'; } throw new AmtException();
2、異步異常
try-catch 代碼塊不能捕獲到異步異常,使用 await 關鍵字聲明的同步調用,屬於同步異常范圍,可以通過 try-catch 捕獲。
使用 catchError 捕獲異步異常,第一個參數為 Function error 類型,第二個參數為 {bool test(Object error)},是一個判斷表達式,當此表達式返回值為 true 時,表示需要執行 catch 邏輯,如果返回 false,則不執行 catch 邏輯,即會成為未捕獲的異常,默認不傳時 認為是true。
這里的作用是可以精細化的處理異常,可以理解為同步異常中強化版的 on 關鍵字,
入參至多兩個 分別為error 和 stack,均可選。
Future(() {
}).then((value){ }).catchError((error, stack) { }); Future.delayed(Duration(seconds: 1)).then((e) => Future.error("xxx"));
二、Flutter異常捕獲、拋出
為了捕獲並上報異常,可以把應用運行在一個自定義的 Zone 里面。 Zones 為代碼建立執行上下文環境。在這個上下文環境中,所有發生的異常在拋出 onError 時都能夠很容易地被捕獲到。
Dart中有一個runZoned(...) 方法,可以給執行對象指定一個Zone。Zone表示一個代碼執行的環境范圍,為了方便理解,讀者可以將Zone類比為一個代碼執行沙箱,不同沙箱的之間是隔離的,沙箱可以捕獲、攔截或修改一些代碼行為,如Zone中可以捕獲日志輸出、Timer創建、微任務調度的行為,同時Zone也可以捕獲所有未處理的異常。下面我們看看runZoned(...)方法定義:
R runZoned<R>(R body(), {
Map zoneValues,
ZoneSpecification zoneSpecification,
Function onError,
})
參數含義可參考《Flutter實戰》https://book.flutterchina.club/chapter2/thread_model_and_error_report.html
在下面的例子中,將會把應用運行在一個新的 Zone 里面並捕獲所有錯誤,在 1.17 之前的 Flutter 版本里,你可以通過 onError() 回調捕獲所有的異常。
runZoned<Future<void>>(() async { runApp(MyApp()); }, onError: (error, stackTrace) { // Whenever an error occurs, call the `_reportError` function. This sends // Dart errors to the dev console or Sentry depending on the environment. _reportError(error, stackTrace); });
在包含了 Dart 2.8 的 Flutter 1.17 中,使用 runZonedGuarded:
runZonedGuarded<Future<void>>(() async { runApp(MyApp()); }, (Object error, StackTrace stackTrace) { // Whenever an error occurs, call the `_reportError` function. This sends // Dart errors to the dev console or Sentry depending on the environment. _reportError(error, stackTrace); });
除了 Dart 異常,Flutter 也能拋出其他的異常,比如調用原生代碼發生的平台異常。這種類型的異常也同樣是需要上報的。
為了捕獲 Flutter 異常,需要重寫 FlutterError.onError 屬性。在開發環境下,可以將異常格式化輸出到控制台。在生產環境下,可以把異常傳遞給上個步驟中的 onError 回調。
大致步驟如下:
Future<Null> main() async { //重寫FlutterError.onError FlutterError.onError = (FlutterErrorDetails details) { if (isInDebugMode) { // In development mode, simply print to console. FlutterError.dumpErrorToConsole(details); } else { // In production mode, report to the application zone to report to // Sentry. Zone.current.handleUncaughtError(details.exception, details.stack); } }; //使用runZonedGuarded捕獲日志輸出、Timer創建、微任務調度的行為、所有未處理的異常 runZonedGuarded<Future<Null>>( () async { runApp(MyApp()); }, (error, stackTrace) async { await _reportErrorAndLog(error, stackTrace); }, zoneSpecification: ZoneSpecification( print: (Zone self, ZoneDelegate parent, Zone zone, String line) async { await collectLog(line); // 收集日志 }, ), ); } Future<Null> collectLog(String line) async { //收集日志 } //上報錯誤和日志邏輯 Future<Null> _reportErrorAndLog(dynamic error, dynamic stackTrace) async { print('Caught error: $error'); if (isInDebugMode) { print(stackTrace); print('In dev mode. Not sending report to Sentry.io.'); return; } //TODO: response 為上傳請求結果 if (response.isSuccessful) { print('Success!'); } else { print('Failed to report to Sentry.io: ${response}'); } } //判斷debug及release環境 bool get isInDebugMode { bool inDebugMode = false; assert(inDebugMode = true); return inDebugMode; }
參考:
https://book.flutterchina.club/chapter2/thread_model_and_error_report.html
https://flutter.cn/docs/cookbook/maintenance/error-reporting#5-catch-and-report-dart-errors
https://juejin.cn/post/6906274131394691085