技术实践第二期|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.exceptiondetails.stack);

   };

   runApp(MyApp());

 }, (Object error, StackTrace stack) {

   myErrorsHandler.onError(error, stack);

 });

代码中出现了一句,上诉从没有出现过的代码即WidgetsFlutterBinding.ensureInitialized(),当我把这行代码注释掉的时候,框架异常是捕获不到的。

当时困扰了好久最后终于查到了原因:

Dingtalk_20211203145217.jpg

上图是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];

}

以上就是本期干货内容的介绍,希望我们的技术内容可以更好地帮助开发者们解决问题,我们将陪伴开发者们一起进步,一起成长。


点击下面链接加入友盟+ 技术社群

https://q-187234529230979072.a.lingyiliebian.com/code/

与超过1000+移动开发者共同讨论移动开发最新动态

欢迎点击【友盟+】,了解友盟+ 最新移动技术
欢迎关注【友盟全域数据】公众号

 

相关文章
|
7月前
|
缓存 监控 前端开发
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
【4月更文挑战第30天】本文探讨了Flutter应用启动优化策略,包括理解启动过程、资源加载优化、减少初始化工作、界面布局简化、异步初始化、预加载关键数据、性能监控分析以及案例和未来优化方向。通过这些方法,可以缩短启动时间,提升用户体验。使用Flutter DevTools等工具可助于识别和解决性能瓶颈,实现持续优化。
296 0
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
|
4月前
Flutter-解决Try catch出现异常:type ‘_TypeError‘ is not a subtype of type ‘Exception‘ in type cast
Flutter-解决Try catch出现异常:type ‘_TypeError‘ is not a subtype of type ‘Exception‘ in type cast
101 1
|
4月前
|
SQL 分布式计算 大数据
Flutter技术实践问题之Flutter应用过程中的基础建设如何解决
Flutter技术实践问题之Flutter应用过程中的基础建设如何解决
36 10
|
4月前
|
新零售 前端开发 小程序
Flutter技术实践问题之基于Flutter的Canvas的应用优势如何解决
Flutter技术实践问题之基于Flutter的Canvas的应用优势如何解决
43 2
|
4月前
|
Web App开发 新零售 前端开发
Flutter技术实践问题之阿里集团内Flutter体系化建设如何解决
Flutter技术实践问题之阿里集团内Flutter体系化建设如何解决
49 1
|
4月前
|
Dart JavaScript 前端开发
Dart或Flutter中解决异常-type ‘int‘ is not a subtype of type ‘double‘
Dart或Flutter中解决异常-type ‘int‘ is not a subtype of type ‘double‘
166 4
|
4月前
|
Kubernetes Cloud Native 搜索推荐
探索云原生技术:Kubernetes入门与实践打造个性化安卓应用:从零开始的Flutter之旅
【8月更文挑战第31天】云原生技术正改变着应用开发和部署的方式。本文将带你了解云原生的基石——Kubernetes,通过实际的代码示例,从安装到部署一个简单的应用,让你迅速掌握Kubernetes的核心概念和操作方法。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你进入云原生世界的桥梁。
|
5月前
|
设计模式 编解码 API
Flutter UI设计模式与实现:深入探索与实践
【7月更文挑战第20天】Flutter以其独特的声明式UI模式和丰富的UI组件库,为移动应用开发提供了强大的支持。通过深入理解Flutter的UI设计模式和实现技巧,开发者可以构建出高性能、可维护性强的UI界面。同时,随着Flutter生态的不断完善和发展,相信未来Flutter将在移动应用开发领域发挥更加重要的作用。
|
6月前
|
Dart 监控 测试技术
在Flutter开发中,注重代码质量与重构实践显得尤为重要
【6月更文挑战第11天】随着Flutter在跨平台开发的普及,保持高质量代码成为开发者关注的重点。良好的代码质量关乎应用性能、稳定性和开发效率。为提升Flutter代码质量,开发者应遵循最佳实践,编写可读性高的代码,实施代码审查和自动化测试。重构实践在应对代码复杂性时也至关重要,包括识别重构时机、制定计划、逐步操作及利用重构工具。注重代码质量和重构是Flutter开发成功的关键。
86 3
|
7月前
|
Dart 前端开发 安全
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践
【4月更文挑战第30天】本文探讨了Flutter中线程管理和并发编程的关键性,强调其对应用性能和用户体验的影响。Dart语言提供了`async`、`await`、`Stream`和`Future`等原生异步支持。Flutter采用事件驱动的单线程模型,通过`Isolate`实现线程隔离。实践中,可利用`async/await`、`StreamBuilder`和`Isolate`处理异步任务,同时注意线程安全和性能调优。参考文献包括Dart异步编程、Flutter线程模型和DevTools文档。
244 1
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践