开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情

如果你正需要处理Flutter异常捕获,那么恭喜你,找对地了,这里从根源上给你准备了Flutter异常捕获需要是所有知识和原理,让你更深刻认识Flutter Zone概念。

Zone是什么

/// A zone represents an environment that remains stable across asynchronous
/// calls.

SDK中描述:表示一个环境,这个环境为了保持稳定异步调用。

通俗理解39 | 线上出现问题,该如何做好异常捕获与信息采集?中描述:

我们可以给代码执行对象指定一个 Zone,在 Dart 中,Zone 表示一个代码执行的环境范围,其概念类似沙盒,不同沙盒之间是互相隔离的。如果我们想要观察沙盒中代码执行出现的异常,沙盒提供了 onError 回调函数,拦截那些在代码执行对象中的未捕获异常。

Zone创建

Dart提供了runZoned方法,支持Zone的快速创建

R runZoned<R>(R body(),
  {Map<Object?, Object?>? zoneValues,
   ZoneSpecification? zoneSpecification,
   @Deprecated("Use runZonedGuarded instead") Function? onError}) {
  • zoneValues: Zone 的私有数据,可以通过实例zone[key]获取,可以理解为每个“沙箱”的私有数据。
  • zoneSpecification:Zone的一些配置,可以自定义一些代码行为,比如拦截日志输出和错误等

Zone的作用

捕获异常

import 'dart:async';

//OUTPUT:Uncaught error: Would normally kill the program
void main() {
runZonedGuarded(() {
Timer.run(() {
throw 'Would normally kill the program';
});
}, (error, stackTrace) {
print('Uncaught error: $error');
});
}

用try catch一样可以捕获,为啥要通过Zone来捕获?

  1. Zone回调收拢了异步捕获入口,提高了可维护性。
  2. 未预料的未捕获异常可以帮你自动捕获到,提高便捷性。

是不是所有异常都可以捕获到?

不是, 只能处理情况1。

  1. Zone默认捕获范围主要针对异步异常或者一般逻辑异常等常规异常,比如Future中出了问题,或者逻辑处理了1/0,(见Tag3),捕获异步异常原理见简话-Flutter异常处理 - 掘金

  2. Dart中另外比较容易出现的异常是framework异常,比如build异常等,这种异常Zone无法捕获到,原因可以参看Flutter异常捕获和Crash崩溃日志收集 。如果想Zone来处理可这样抛给它(见Tag1)

  3. Flutter Engine和Native异常,isolate异常 不是runZonedGuarded和FlutterError.onError 能处理范围。

  4. isolate异常处理(见Tag2)

    原理参考特别放送 | 温故而知新,与你说说专栏的那些思考题

并发 Isolate 的异常是无法通过 try-catch 来捕获的。并发 Isolate 与主 Isolate 通信是采用 SendPort 的消息机制,而异常本质上也可以视作一种消息传递机制。所以,如果主 Isolate 想要捕获并发 Isolate 中的异常消息,可以给并发 Isolate 传入 SendPort。而创建 Isolate 的函数 spawn 中就恰好有一个类型为 SendPort 的 onError 参数,因此并发 Isolate 可以通过往这个参数里发送消息,实现异常通知。

完整Dart异常捕获代码

void main() {
FlutterError.onError = (FlutterErrorDetails details) {
Zone.current.handleUncaughtError(details.exception, details.stack);//Tag1
//或customerReport(details);
}; //Tag2
Isolate.current.addErrorListener(
RawReceivePort((dynamic pair) async {
final isolateError = pair as List<dynamic>;
customerReport(details);
}).sendPort,
); runZoned(
() => runApp(MyApp()),
zoneSpecification: ZoneSpecification(
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
report(line)
},
),
onError: (Object obj, StackTrace stack) {
//Tag3
customerReport(e, stack);
}
);
}

在部分或全部代码中覆盖一组有限的方法

例如print()scheduleMicrotask()

main() {
runZoned(() {
print("test");
}, zoneSpecification: ZoneSpecification(
print: (self, parent, zone, s) {
parent.print(zone, "hook it: $s");
}
));
} //OUTPUT:hook it: test

上面实现的原理是什么呢?

简单讲就是runZoned从root Zone fork了一个子Zone,print打印时如果当前Zone

不为空则使用当前Zone的print来打印,而不使用root Zone的print方法。详细见Dart中Future、Zone、Timer的源码学习

每次代码进入或退出区域时执行一个操作

例如启动或停止计时器,或保存堆栈跟踪。

如下例子,Zone提供了一个hook点,在执行其中方法时候,可以做额外包装操作(Tag1,Tag2),比如耗时方法打印,这样在不破坏原有代码基础上实现了无侵入的统一逻辑注入。

import 'dart:async';

final total = new Stopwatch();
final user = new Stopwatch(); final specification = ZoneSpecification(run: <R>(self, parent, zone, f) {
//Tag1
user.start();
try {
return parent.run(zone, f);
} finally {
//Tag2
user.stop();
}
}); void main() {
runZoned(() {
total.start();
a();
b();
c().then((_) {
print(total.elapsedMilliseconds);
print(user.elapsedMilliseconds);
});
}, zoneSpecification: specification);
} void a() {
print('a');
} void b() {
print('b');
} Future<void> c() {
return Future.delayed(Duration(seconds: 5), () => print('c'));
}

输出:

a

b

c

5005

6

将数据(称为 Zone本地值)与各个其他Zone相关联

这个作用类似java中的threadlocal,每个Zone相当于有自己值的作用范围,Zone直接值的传递和共享通过zonevalue来实现。

import 'dart:async';

void main() {
Zone firstZone = Zone.current.fork(zoneValues: {"name": "bob"});
Zone secondZone = firstZone.fork(zoneValues: {"extra_values": 12345});
secondZone.run(() {
print(secondZone["name"]); // bob
print(secondZone["extra_values"]); // 12345
});
}

案例说明:

和Linux类似地,当Zone做Fork的时候,会将父Zone所持有的ZoneSpecification、ZoneValues会继承下来,可以直接使用。并且是支持追加的,secondZone在firstZone的基础之上,又追加了extra_values属性,不会因为secondZone的ZoneValues就导致name属性被替换掉。

参考链接

简话-Flutter异常处理 - 掘金

Zones | Dart

Brian Ford - Zones - NG-Conf 2014 - YouTube

[Flutter] 认识Zone和异常处理 - 掘金

2.8 Flutter异常捕获 | 《Flutter实战·第二版》

特别放送 | 温故而知新,与你说说专栏的那些思考题

欢迎搜索公众号:【码里特别有禅】 里面整理收集了最详细的Flutter进阶与优化指南。关注我,获取我的最新文章~

Flutter异常监控 - 壹 | 从Zone说起的更多相关文章

  1. 【原】聊聊js代码异常监控

    在平时的工作,js报错是比较常见的一个情景,尤其是有一些错误可能我们在本地测试的时候测试不出来,当发布到线上之后才可以发现,如果抢救及时,那还好,假如很晚才发 现,那就可能造成很大的损失了.如果我们前 ...

  2. Sencha Touch 2.2 Store Proxy 异常监控

    移动端到服务端通信往往会发生很多莫名的异常情况,如何有效的监控proxy异常,给用户友好的用户体验呢? Proxy给我提供了异常exception的监听事件,只需要监控该项目即可. Sencha To ...

  3. 从无到有<前端异常监控系统>落地

    导火索 有一天一个测试同事的一个移动端页面白屏了,看样子是页面哪里报错了.  我自己打开页面并没有报错,最后发现报错只存在于他的手机,移动端项目又是在微信环境下,调试起来会比较麻烦,最后用他手机调试才 ...

  4. 业务线接入前端异常监控sentry

    1.前端异常处理的框架对比   是否开源 收费 语言 监控范围 sentry 是 自己搭建服务器(免费)价格 英文 Angular.AngularJs.Backbone.Ember.JavaScrip ...

  5. Fundebug后端Java异常监控插件更新至0.2.0,支持Spring及Maven

    摘要: 0.2.0支持监控Spring应用,并且支持使用Maven接入插件,请大家及时更新. 支持监控Spring应用 1. pom.xml配置fundebug-spring依赖 <depend ...

  6. 异常: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configurat

    异常: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. ...

  7. 前端异常监控 - BadJS

    前端异常监控 - BadJS 简介:BadJS 是 web 前端异常监控解决方案,提供一种 web 页面的脚本错误监控.上报.统计.查看等系统化的跟踪解决方案.目前BadJS覆盖了腾讯课堂.公众号.邮 ...

  8. Asp.net Core全局异常监控和记录日志

    前言           系统异常监控可以说是重中之重,系统不可能一直运行良好,开发和运维也不可能24小时盯着系统,系统抛异常后我们应当在第一时间收到异常信息.在Asp.net Core里我使用拦截器 ...

  9. Fundebug前端异常监控插件更新至2.0.0,全面支持TypeScript

    摘要: 是时候支持TS了! Fundebug前端异常监控服务 Fundebug提供专业的前端异常监控服务,我们的插件可以提供全方位的异常监控,可以帮助开发者第一时间定位各种前端异常,包括但不限于Jav ...

  10. Fundebug前端异常监控插件更新至 1.9.0,支持监控 HTTP 慢请求

    摘要: 1.9.0新增 httpTimeout 配置选项,支持监控 HTTP 慢请求,同时修复了记录的 HTTP 响应时间偏小的 BUG. Fundebug提供专业的前端异常监控服务,可以第一时间捕获 ...

随机推荐

  1. 个人音乐博客 h5、css和js等

    浅说一下吧 这个小项目由h5和css还有js和jq写的 主题内容为个人音乐 博客等 首页一级导航栏 以及侧边栏 整合部分图标(侧边栏未添加收起操作 时间原因 会的朋友们可以自行添加一个动画就可以 在m ...

  2. capability参数配置

    前言 本文为在霍格沃兹测试开发学社的学习经历分享 简介 功能:配置 Appium 会话,告诉 Appium 服务器需要自动化的平台的应用程序 形式:键值对的集合,键对应设置的名称,值对应设置的值 主要 ...

  3. ubuntu20.04 利用xrandr命令修改多显示器分辨率

    问题描述 笔记本是ThinkPad X1 Extreme Gen3 4K屏,外接了一个27寸的1080P显示器.目标是让两个显示器的"显示效果"分辨率能一致,就如winwods和m ...

  4. 将Java代码打包成jar文件转换为.exe可执行程序方法

    ​ 做完学生管理系统后我想将代码打包成一个可执行程序,那样就可以直接点击在Windows下运行了,下面就跟大家分享下打包方法. 将Java的代码转换成 .exe 文件需要先把代码打包成 jar 文件然 ...

  5. java连接数据库加载驱动到java项目

    java连接数据库 package com.cook.zheng; import java.sql.Connection; import java.sql.DriverManager; public ...

  6. 3.CBV视图之csrf补充

    CBV使用csrf装饰器关闭/开启 csrf验证,直接在函数上加装饰器无效的 #方法1 from django.views import View from django.views.decorato ...

  7. 关于引用JS和CSS文件刷新浏览器缓存问题,部署服务器后客户端样式不刷新

    问题描述 对样式的css文件进行了修改,部署到服务器后访问发现页面展示不正常,但是刷新之后就会展示正常. 问题分析 研究之后发现可能的原因有 css文件过大,加载缓慢 本地缓存问题,虽然服务器修改了c ...

  8. 二十二、Pod存储之volume

    Pod 的存储之volume ​容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题.首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失--容器以干净的状 ...

  9. Jmeter之聚合报告“造假”

    通过Jmeter,模拟一个"虚假"的聚合报告,可"应付"日常现场项目的性能测试验收.本文档着重介绍jmeter的固定定时器,通过设置随机的延迟时间(如想业务场景 ...

  10. 嵌入式-C语言基础:数组得初始化

    #include<stdio.h> int main() { int a[10]; int size=sizeof(a)/sizeof(a[0]);//计算数组得大小 for(int i= ...