美文网首页
Flutter异常捕获runZoned

Flutter异常捕获runZoned

作者: waiwaaa | 来源:发表于2020-04-17 16:30 被阅读0次

    今天开始看gsy_github_app_flutter
    入口文件就来了个runZoned,代码如下

    void main() {
      runZoned(() {
        ErrorWidget.builder = (FlutterErrorDetails details) {
          Zone.current.handleUncaughtError(details.exception, details.stack);
          return ErrorPage(
              details.exception.toString() + "\n " + details.stack.toString(), details);
        };
        runApp(ConfigWrapper(
          child: FlutterReduxApp(),
          config: EnvConfig.fromJson(config),
        ));
      }, onError: (Object obj, StackTrace stack) {
        print(obj);
        print(stack);
      });
    }
    

    ErrorWidget.builder

    Flutter就会自动弹出一个错误界面,这是因为Flutter已经在执行build方法时添加了异常捕获,最终的源码如下:

    @override
    void performRebuild() {
    ...
    try {
    //执行build方法
    built = build();
    } catch (e, stack) {
    // 有异常时则弹出错误提示
    built = ErrorWidget.builder(_debugReportException('building $this', e, stack));
    }
    ...
    }
    可以看到,在发生异常时,Flutter默认的处理方式时弹一个ErrorWidget
    Flutter就会自动弹出一个错误界面,这是因为Flutter已经在执行build方法时添加了异常捕获,最终的源码如下:

    @override
    void performRebuild() {
     ...
      try {
        //执行build方法  
        built = build();
      } catch (e, stack) {
        // 有异常时则弹出错误提示  
        built = ErrorWidget.builder(_debugReportException('building $this', e, stack));
      } 
      ...
    }      
    

    可以看到,在发生异常时,Flutter默认的处理方式时弹一个ErrorWidget,所以ErrorWidget.builder就是用于生成widget时错误时替换默认错误显示的,就是替换那个全红的报错界面。

    image.png

    runZoned

    在Flutter中,还有一些Flutter没有为我们捕获的异常,如调用空对象方法异常、Future中的异常。在Dart中,异常分两类:同步异常和异步异常,同步异常可以通过try/catch捕获,而异步异常则比较麻烦,如下面的代码是捕获不了Future的异常的:

    try{
        Future.delayed(Duration(seconds: 1)).then((e) => Future.error("xxx"));
    }catch (e){
        print(e)
    }
    

    Dart中有一个runZoned(...)方法,可以给执行对象指定一个ZoneZone表示一个代码执行的环境范围,为了方便理解,读者可以将Zone类比为一个代码执行沙箱,不同沙箱的之间是隔离的,沙箱可以捕获、拦截或修改一些代码行为,如Zone中可以捕获日志输出、Timer创建、微任务调度的行为,同时Zone也可以捕获所有未处理的异常。下面我们看看runZoned(...)方法定义:

    R runZoned<R>(R body(), {
        Map zoneValues, 
        ZoneSpecification zoneSpecification,
        Function onError,
    }) 
    

    zoneValues: Zone 的私有数据,可以通过实例zone[key]获取,可以理解为每个“沙箱”的私有数据。

    zoneSpecification:Zone的一些配置,可以自定义一些代码行为,比如拦截日志输出行为等,举个例子:

    下面面是拦截应用中所有调用print输出日志的行为。

    main() {
    runZoned(() => runApp(MyApp()), zoneSpecification: new ZoneSpecification(
        print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
          parent.print(zone, "Intercepted: $line");
        }),
    );
    }
    

    这样一来,我们APP中所有调用print方法输出日志的行为都会被拦截,通过这种方式,我们也可以在应用中记录日志,等到应用触发未捕获的异常时,将异常信息和日志统一上报。ZoneSpecification还可以自定义一些其他行为,读者可以查看API文档。

    onError:Zone中未捕获异常处理回调,如果开发者提供了onError回调或者通过ZoneSpecification.handleUncaughtError指定了错误处理回调,那么这个zone将会变成一个error-zone,该error-zone中发生未捕获异常(无论同步还是异步)时都会调用开发者提供的回调,如:

    runZoned(() {
      runApp(MyApp());
    }, onError: (Object obj, StackTrace stack) {
      var details=makeDetails(obj,stack);
      reportError(details);
    });
    

    这样一来,结合上面的FlutterError.onError我们就可以捕获我们Flutter应用中全部错误了!需要注意的是,error-zone内部发生的错误是不会跨越当前error-zone的边界的,如果想跨越error-zone边界去捕获异常,可以通过共同的“源”zone来捕获,如:

    var future = new Future.value(499);
    runZoned(() {
    var future2 = future.then((_) { throw "error in first error-zone"; });
    runZoned(() {
        var future3 = future2.catchError((e) { print("Never reached!"); });
    }, onError: (e) { print("unused error handler"); });
    }, onError: (e) { print("catches error of first error-zone."); }
    );
    

    本文资料来源

    相关文章

      网友评论

          本文标题:Flutter异常捕获runZoned

          本文链接:https://www.haomeiwen.com/subject/crcyvhtx.html