乐趣区

关于flutter:技术实践第二期|Flutter异常捕获

简介:利用性能稳固是良好用户体验中十分要害的一环,为了更好保障利用性能稳固,异样捕捉在保障线上产品稳固中扮演着至关重要的角色。咱们团队在推出了 U -APM 挪动利用性能监控的产品后,帮忙开发者定位并解决掉很多线上的疑难杂症。随着应用人数的增多,关注度的进步,在访问客户和开发者的留言中,很多开发者都提出心愿该产品能够反对 flutter 框架的异样捕捉。自身我并没有做过 flutter 开发,所以次要是通过在现有产品能力根底上做插件实现异样的上报,这篇文章就记录我学习 flutter 错误处理的过程和遇到的问题。

作者:友盟 + 技术专家 彦克

一、背景

利用性能稳固是良好用户体验中十分要害的一环,为了更好保障利用性能稳固,异样捕捉在保障线上产品稳固中扮演着至关重要的角色。咱们团队在推出了 U -APM 挪动利用性能监控的产品后,帮忙开发者定位并解决掉很多线上的疑难杂症。随着应用人数的增多,关注度的进步,在访问客户和开发者的留言中,很多开发者都提出心愿该产品能够反对 flutter 框架的异样捕捉。自身我并没有做过 flutter 开发,所以次要是通过在现有产品能力根底上做插件实现异样的上报,这篇文章就记录我学习 flutter 错误处理的过程和遇到的问题。

二、Flutter 异样

Flutter 异样指的是,Flutter 程序中 Dart 代码运行时意外产生的谬误事件。

三、Flutter 异样特点

Dart 是单过程机制,所以在这个过程中呈现问题时仅仅会影响以后过程,Dart 采纳事件循环的机制来运行工作,当某个工作产生异样并没有被捕捉时,程序并不会退出,而间接导致的后果是当前任务的后续代码就不会被执行了,也就是说一个工作中的异样是不会影响其它工作执行的,各个工作的运行状态是相互独立的。

如:咱们能够通过与 Java 相似的 try-catch 机制来捕捉它。但与 Java 不同的是,Dart 程序不强制要求咱们必须解决异样。

四、Flutter 异样分类

在 Flutter 开发中,依据异样起源的不同,能够将异样分为 Framework 异样和 App 异样。Flutter 对这两种异样提供了不同的捕捉形式,Framework 异样是由 Flutter 框架引发的异样,通常是因为谬误的利用代码造成 Flutter 框架底层的异样判断引起的。而对于 App 异样,就是利用代码的异样,通常由未解决应用层其余模块所抛出的异样引起。依据异样代码的执行时序,App 异样能够分为两类,即同步异样和异步异样。

五、捕捉形式

1.App 异样的捕捉形式

捕捉同步异样应用 try-catch 机制:

// 应用 try-catch 捕捉同步异样

try {throw StateError('This is a Dart exception.');

}

catch(e) {print(e);

}

捕捉异步异样应用 Future 提供的 catchError 语句:

// 应用 catchError 捕捉异步异样

Future.delayed(Duration(seconds: 1))

    .then((e) => throw StateError('This is a Dart exception in Future.'))

    .catchError((e)=>print(e));

看到这里预计很多人心里会问,就不能有一种形式既能够监控同步又能够监控异步异样吗?

答案是有的。

Flutter 提供了 Zone.runZoned 办法来治理代码中的所有异样。咱们能够给代码执行对象指定一个 Zone,在 Dart 中,Zone 示意一个代码执行的环境范畴,其概念相似沙盒,不同沙盒之间是相互隔离的。如果咱们想要察看沙盒中代码执行呈现的异样,沙盒提供了 onError 回调函数,拦挡那些在代码执行对象中的未捕捉异样。废话不多说,

Show me the code!runZoned(() {

  // 同步异样

  throw StateError('This is a Dart exception.');

}, onError: (dynamic e, StackTrace stack) {print('Sync error caught by zone');

});

runZoned(() {

  // 异步异样

  Future.delayed(Duration(seconds: 1))

      .then((e) => throw StateError('This is a Dart exception in Future.'));

}, onError: (dynamic e, StackTrace stack) {print('Async error aught by zone');

});

为了可能集中捕捉 Flutter 利用中的未解决异样,最终我把 main 函数中的 runApp 语句也搁置在 Zone 中。这样在检测到代码中运行异样时,就能依据获取到的异样上下文信息,进行对立解决了:

runZoned>(() async {runApp(MyApp());

}, onError: (error, stackTrace) async {//Do sth for error});

2.Framework 异样捕捉形式

Flutter 框架为咱们在很多要害的办法进行了异样捕捉。如果咱们想本人上报异样,只须要提供一个自定义的错误处理回调即可,如:

void main() {FlutterError.onError = (FlutterErrorDetails details) {reportError(details);

  };

 ...

}

有没有一套从天而降的代码,可能对立解决以上异样呢?

3. 总结(一套代码捕捉所有异样)

runZonedGuarded(() async {WidgetsFlutterBinding.ensureInitialized();

FlutterError.onError = (FlutterErrorDetails details) {myErrorsHandler.onError(details.exception,details.stack);

    };

    runApp(MyApp());

  }, (Object error, StackTrace stack) {myErrorsHandler.onError(error, stack);

  });

代码中呈现了一句,上诉从没有呈现过的代码即 WidgetsFlutterBinding.ensureInitialized(),当我把这行代码正文掉的时候,框架异样是捕捉不到的。

过后困扰了良久最初终于查到了起因:

上图是 Flutter 的架构层,WidgetFlutterBinding 用于与 Flutter 引擎交互。咱们的 APM 产品须要调用 native 代码来初始化,并且因为插件须要应用平台 channel 来调用 native 代码,这是异步实现的,因而必须调用 ensureInitialized()确保你有一个 WidgetsBinding 的实例.

来自 docs :

Returns an instance of the WidgetsBinding, creating and initializing it if necessary. If one is created, it will be a WidgetsFlutterBinding. If one was previously initialized, then it will at least implement WidgetsBinding.

注:如果你的利用在 runApp 中调用了 WidgetsFlutterBinding.ensureInitialized() 办法来进行一些初始化操作,则必须在 runZonedGuarded 中调用 WidgetsFlutterBinding.ensureInitialized()

六、异样上报

异样上报的整体计划是通过已有的插件减少接口,桥接 Android APM 和 iOS APM 库的自定义异样上报接口。

插件减少函数

static void postException(error, stack) {List args = [error,stack];

    // 将异样和堆栈上报至 umapm

    _channel.invokeMethod("postException",args);

  }

Android 端调用自定义异样上报:

private void postException(List args){String error = (String)args.get(0);

    String stack = (String)args.get(1);

    UMCrash.generateCustomLog(stack,error);

  }

iOS 端调用自定义异样上报:

if ([@"postException" isEqualToString:call.method]){NSString* error = arguments[0];

        NSString* stack = arguments[1];

        [UMCrash reportExceptionWithName:@"Flutter" reason:error stackTrace:stack terminateProgram:NO];

 }

以上就是本期干货内容的介绍,心愿咱们的技术内容能够更好地帮忙开发者们解决问题,咱们将陪伴开发者们一起提高,一起成长。

原文链接
本文为阿里云原创内容,未经容许不得转载。

退出移动版