Flutter 中的Error的捕获及处理
前言
Flutter 框架可以捕獲運(yùn)行期間的錯(cuò)誤,包括構(gòu)建期間、布局期間和繪制期間。
所有 Flutter 的錯(cuò)誤均會(huì)被回調(diào)方法?FlutterError.onError?捕獲。默認(rèn)情況下,會(huì)調(diào)用?FlutterError.dumpErrorToConsole?方法,正如方法名表示的那樣,將錯(cuò)誤轉(zhuǎn)儲(chǔ)到當(dāng)前的設(shè)備日志中。當(dāng)從 IDE 運(yùn)行應(yīng)用時(shí),檢查器重寫了該方法,錯(cuò)誤也被發(fā)送到 IDE 的控制臺(tái),可以在控制臺(tái)中檢查出錯(cuò)的對(duì)象。
當(dāng)構(gòu)建期間發(fā)生錯(cuò)誤時(shí),回調(diào)函數(shù)?ErrorWidget.builder?會(huì)被調(diào)用,來生成一個(gè)新的 widget,用來代替構(gòu)建失敗的 widget。默認(rèn)情況,debug 模式下會(huì)顯示一個(gè)紅色背景的錯(cuò)誤頁面, release 模式下會(huì)展示一個(gè)灰色背景的空白頁面。
如果在調(diào)用堆棧上沒有 Flutter 回調(diào)的情況下發(fā)生錯(cuò)誤(這里可以理解為FlutterError.onError僅僅可以捕獲主線程的錯(cuò)誤,而其他異步線程的錯(cuò)誤則需要Zone來捕獲),它們由發(fā)生區(qū)域的?Zone?處理。?Zone?在默認(rèn)情況下僅會(huì)打印錯(cuò)誤,而不會(huì)執(zhí)行其他任何操作。
這些回調(diào)方法都可以被重寫,通常在?void main()?方法中重寫。
下面來看看如何處理。
捕獲Flutter錯(cuò)誤
重寫FlutterError的onError即可,如下:
import 'dart:io';import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart';void main() {FlutterError.onError = (FlutterErrorDetails details) {FlutterError.dumpErrorToConsole(details);if (kReleaseMode)... //處理線上錯(cuò)誤,如統(tǒng)計(jì)上傳};runApp(MyApp()); }上面我們重寫了FlutterError.onError,這樣就可以捕獲到錯(cuò)誤,第一行代碼就是將error展示到控制臺(tái),這樣我開發(fā)時(shí)就會(huì)在控制臺(tái)很方便的看到錯(cuò)誤。下面代碼就是在線上環(huán)境下,對(duì)錯(cuò)誤進(jìn)一步處理,比如統(tǒng)計(jì)上傳。
自定義ErrorWidget
上面我們知道,構(gòu)建時(shí)發(fā)生錯(cuò)誤會(huì)默認(rèn)展示一個(gè)錯(cuò)誤頁面,但是這個(gè)頁面很不友好,我們可以自定義一個(gè)錯(cuò)誤頁面。定義一個(gè)自定義的 error widget,以當(dāng) builder 構(gòu)建 widget 失敗時(shí)顯示,請(qǐng)使用 MaterialApp.builder。
class MyApp extends StatelessWidget { ...@overrideWidget build(BuildContext context) {return MaterialApp(...builder: (BuildContext context, Widget widget) {Widget error = Text('...rendering error...');if (widget is Scaffold || widget is Navigator)error = Scaffold(body: Center(child: error));ErrorWidget.builder = (FlutterErrorDetails errorDetails) => error;return widget;},);} }在App下的builder中,自定義一個(gè)error頁面,然后賦值給ErrorWidget.builder即可。這樣再出現(xiàn)錯(cuò)誤的時(shí)候就可以展示一個(gè)友好的頁面。
無法捕獲的錯(cuò)誤
假設(shè)一個(gè) onPressed 回調(diào)調(diào)用了異步方法,例如 MethodChannel.invokeMethod (或者其他 plugin 的方法):
OutlinedButton(child: Text('Click me!'),onPressed: () async {final channel = const MethodChannel('crashy-custom-channel');await channel.invokeMethod('blah');}, ),如果 invokeMethod 拋出了錯(cuò)誤,它不會(huì)傳遞至 FlutterError.onError,而是直接進(jìn)入 runApp 的 Zone。
如果你想捕獲這樣的錯(cuò)誤,請(qǐng)使用 runZonedGuarded。代碼如下:
import 'dart:async';void main() {runZonedGuarded(() {runApp(MyApp());}, (Object error, StackTrace stack) {... //處理錯(cuò)誤}); }請(qǐng)注意,如果你的應(yīng)用在 runApp 中調(diào)用了 WidgetsFlutterBinding.ensureInitialized() 方法來進(jìn)行一些初始化操作(例如 Firebase.initializeApp()),則必須在 runZonedGuarded 中調(diào)用 WidgetsFlutterBinding.ensureInitialized():
runZonedGuarded(() async {WidgetsFlutterBinding.ensureInitialized();await Firebase.initializeApp();runApp(MyApp()); }如果 `WidgetsFlutterBinding.ensureInitialized()` 在外部調(diào)用,錯(cuò)誤將不會(huì)被捕獲到。
完整處理流程
如果要處理上面全部問題,則先通過runZonedGuarded處理異步錯(cuò)誤,再通過FlutterError.onError處理,這些錯(cuò)誤都通過一個(gè)我們自定義的MyErrorsHandler類來集中處理即可,比如統(tǒng)計(jì)上傳等。然后在app中還需要定義一個(gè)友好的錯(cuò)誤頁面。
源碼
關(guān)注公眾號(hào):BennuCTech,發(fā)送“FlutterError”獲取完整源碼。
?
總結(jié)
以上是生活随笔為你收集整理的Flutter 中的Error的捕获及处理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Flutter如何与Native(And
- 下一篇: RecyclerView局部刷新机制——