Flutter Dart回顧 —— 異常捕獲、拋出


  一、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 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM