前言

在上篇 「Sentry 在百瓶的落地实践」中,笔者主要从方案选型 & 落地实践两个大的方面进行了阐述,本篇文章我们主要对 Sentry 在百瓶的落地实践中遇到的问题进行分析。本文中主要分析的问题主要包括以下几大类(Flutter SDK 版本为 1.22.6,Dart SDK 版本为 2.10.5):

  • NoSuchMethodError
  • Flutter 官方 bug (已经修复)
  • StateError
  • NetworkError(DNS)

NoSuchMethodError

问题一

问题描述:

在进行 List 、String 等类型数据判空处理时,直接使用 xxx.isNotEmpty,没有进行判断是否为 null,导致 NoSuchMethodError:The getter isNotEmpty was called null。

问题截图:

<img src=“https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/860fb4fd0f3148049d2e438d383308a8~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image) <img src=“https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/037d0dac18234bd3b73de40a9fdf0fc2~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image” style=“margin: auto” /” style=“margin: auto” />

解决方案:

// 问题代码
if(timeEndList.isNotEmpty){...
}
// 解决方案
static bool isNotNullOrEmpty<E>(Iterable<E> iterable) => iterable != null && iterable.isNotEmpty;if (IterableUtils.isNotNullOrEmpty(timeEndList)){...
}

在进行判空处理时,需要首先判断是否为 null,然后再使用 isNotEmpty 进行判断,避免这种类型错误,考虑到我们在项目中会使用大量类似判断,所以我们可以对同一类型的数据判断方法进行封装,避免每处使用都要再去写一遍。

问题二

问题描述:

这里是使用了 Future.wait 并发请求多个 API,并且在第二个 API 设置超时,由于第二个 API 请求超时,在后续处理响应时,没有处理空异常判断导致获取不到 code。

问题截图:

<img src=“https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a5f359fcd57a4152986e8a853b7500fc~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image) <img src=“https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f85d7d219b99495980cafd89bac3301d~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image” style=“margin: auto” /” style=“margin: auto” />

解决方案:

// 问题代码
if (res[1].code == HttpCode.ok) {...
}// 解决方案
if (res[1]?.code == HttpCode.ok) {...
}

在使用了 Future.wait 并发请求多个 API ,如果有设置超时处理,要考虑到 API 请求超时失败的问题尽量避免这种问题发生。

问题三

问题描述:

当我们需要获取到 与 Widget 上下文相关联的 RenderBox 尺寸或者位置时,发生错误。

问题截图:

<img src=“https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/adff6e8c6cd64df283d1bad7be586213~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image) <img src=“https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8798fc5027414ce8b7f2c936b97f9047~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image” style=“margin: auto” /” style=“margin: auto” />

解决方案:

// 问题代码
if (IterableUtils.isNotNullOrEmpty(ctx.state.details) == true) {final RenderBox renderBox = ctx.state.detailsKey.currentContext.findRenderObject();final Offset postion = renderBox.localToGlobal(Offset.zero);ctx.dispatch(MallGoodsDetailActionCreator.setDetailsOffsetYAction(postion.dy));
}// 解决方案
if (IterableUtils.isNotNullOrEmpty(ctx.state.details) == true) {WidgetsBinding.instance.addPostFrameCallback((_) {final RenderBox renderBox = ctx.state.detailsKey.currentContext.findRenderObject();final Offset postion = renderBox.localToGlobal(Offset.zero);ctx.dispatch(MallGoodsDetailActionCreator.setDetailsOffsetYAction(postion.dy));});
}

发生以上问题的原因是,上下文并没有与我们的 state 进行关联,如果要避免这种情况发生,我们可以在 Widget 渲染完毕后再进行获取 RenderBox 尺寸或者位置。

Flutter 官方 bug (已经修复)

问题描述:

在使用 NestedScrollView 组件时,由于 position.minScrollExtent 可以为空 ,在生产环境中运行会偶现 NoSuchMethodError nested_scroll_view.dart in _NestedScrollCoordinator.hasScrolledBody NoSuchMethodError: The method ‘>’ was called on null. Receiver: null Tried calling: >() 这个问题,目前官方已经解决并且合并到 master 分支。

问题截图:

那么这个问题是如何发生的呢?用官方的原文来解释就是:

1.scheduleAttachRootWidget 将调用 _firstBuild 并新建一个具有空像素的 _NestedScrollPosition;
2.FocusManager 将安排一个微任务;
3.完成 firstBuild 然后刷新 microTask,NestedScrollView 又 dirty 了;
4.scheduleWarmUpFrame 将重建 dirty 节点并触发异常(_NestedScrollPosition 仅在布局后可用)。

解决方案:

// 问题代码
bool get hasScrolledBody {for (final _NestedScrollPosition position in _innerPositions) {assert(position.minScrollExtent != null && position.pixels != null);if (position.pixels > position.minScrollExtent) {return true;}}return false;
}// 解决方案
bool get hasScrolledBody {for (final _NestedScrollPosition position in _innerPositions) {if (!position.hasContentDimensions || !position.hasPixels) {continue;} else if (position.pixels > position.minScrollExtent) {return true;}}return false;
}

StateError

问题描述:

当我们在使用 list.firstWhere 的时候,通常会引发 Bad State: No element 这类问题。

问题截图:

<img src=“https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2b5e5d26b75e4cd49a747909291b74ab~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image) <img src=“https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2f7dc543f513434eba6ad762d27298b8~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image” style=“margin: auto” /” style=“margin: auto” />

解决方案:

// 问题代码
Map<String, String> getInitialSkuById(String skuId, List<Map<String, dynamic>> skuList) {final Map<String, String> selectedKeyValue = <String, String>{};final Map<String, dynamic> selectedSku =skuList.firstWhere((Map<String, dynamic> skuItem) => skuItem['id'] == skuId);if (selectedSku['stockNum'] > 0) {selectedSku.forEach((String k, dynamic v) {if (k.contains('keyStr')) {selectedKeyValue[k] = v;}});}return selectedKeyValue;
}// 解决方案
Map<String, String> getInitialSkuById(String skuId, List<Map<String, dynamic>> skuList) {final Map<String, String> selectedKeyValue = <String, String>{};final Map<String, dynamic> selectedSku = skuList.firstWhere((Map<String, dynamic> skuItem) => skuItem['id'] == skuId,orElse: null,);if (selectedSku != null && selectedSku['stockNum'] > 0) {selectedSku.forEach((String k, dynamic v) {if (k.contains('keyStr')) {selectedKeyValue[k] = v;}});}return selectedKeyValue;
}

在我们使用 list.firstWhere 的时候,通常有匹配不到条件的时候,这个时候就非常有必要使用 orElse 来进行处理这种情况。

下面的代码根据条件筛选为 ‘green’ 的结果值,如果没有的话就返回 ‘No matching color found’,结果输出为:No matching color found。

final List<String> list = <String>['red', 'yellow', 'pink', 'blue'];
final String item = list.firstWhere((String element) => element == 'green',orElse: () => 'No matching color found',
);
print(item); // // No matching color found

如果没有写 orElse 的情况下会抛出异常: Unhandled exception: Bad state: No element。当然如果在 Null safety 版本下,可以直接使用 firstWhereOrNull 方法来进行处理。 下面我们来对比一下 firstWhere 和 firstWhereOrNull 的源码:

 E firstWhere(bool test(E element), {E orElse()?}) {for (E element in this) {if (test(element)) return element;}if (orElse != null) return orElse();throw IterableElementError.noElement();
}T? firstWhereOrNull(bool Function(T element) test) {for (var element in this) {if (test(element)) return element;}return null;
}

firstWhere 会首先进行匹配符合条件的结果,如果没有匹配到,再进行处理 orElse ,如果没有 orElse ,就会抛出异常;firstWhereOrNull 就简单的多了,如果没有匹配到符合条件的值,就会直接返回 null。

NetworkError(DNS)

网络错误是导致网络请求失败的错误条件,每个网络错误都有一个类型,它是一个字符串,每个网络错误都有一个阶段,它描述了错误发生在哪个阶段:

1.dns:DNS 解析过程中发生的错误;
2.connection:安全连接建立期间发生的错误;
3.application:请求和响应传输过程中发生的错误;

问题描述:

在客户端向服务单发起网络请求时,都会经过 DNS 解析的过程,一般情况下都是基于 DNS 协议向运营商 Local DNS 发起解析请求的传统方式,但是这种情况下可能会出现域名劫持和跨网访问的问题,造成域名解析异常。

解决方案:

那么,如果我们的 App 在发起网络请求的时候,发现 DNS 解析失败,我们应该怎么办?当然我们可以接入阿里云云解析 DNS 服务或者腾讯移动解析 HTTP DNS 等服务来更加有效的保障 App、小程序正常访问。

下面我们来一起回顾一下 DNS 相关的知识:

  • 什么是 DNS
  • 域名分层结构
  • DNS 分层结构
  • DNS 解析过程

DNS

DNS 是域名系统 (Domain Name System) 的缩写,是因特网的一项核心服务,它作为可以将域名和 IP 地址互相映射的一个分布式数据库,能够使人更方便的去访问互联网,而不用去记住能够被机器读取的 IP 数串。

域名分层结构

由于因特网的用户数量过多,所有因特网在命名时采用的是层次树状结构的命名方法。 任何一个连接在因特网上的主机或路由器,都有一个唯一的层次结构(域名)。 域名可以划分为各个子域,子域还可以继续划分为子域的子域,这样就形成了顶级域名、主域名、子域名等。

1.“.com” 是顶级域名;
2.“baiping.com” 是主域名(也可称托管一级域名),主要指企页名;
3.“example.baiping.com” 是子域名(也可称为托管二级域名);
4.“www.example.baiping.com” 是子域名的子域(也可称为托管三级域名)。

DNS 分层结构

域名是分层结构,域名 DNS 服务器也是对应的层级结构。有了域名结构,还需要有域名 DNS 服务器去解析域名,且是需要由遍及全世界的域名 DNS 服务器去解析,域名 DNS 服务器实际上就是装有域名系统的主机。

DNS 解析过程

DNS 查询的结果通常会在本地域名服务器中进行缓存,如果本地域名服务器中有缓存的情况下,则会跳过如下 DNS 查询步骤,很快返回解析结果。本地域名服务器没有缓存的情况下,DNS 查询所需的 8 个步骤:

1.用户在 Web 浏览器中输入 “example.com”,则由本地域名服务器开始进行递归查询。
2.本地域名服务器采用迭代查询的方法,向根域名服务器进行查询;
3.根域名服务器告诉本地域名服务器,下一步应该查询的顶级域名服务器 .com TLD(顶级域名服务器)的 IP 地址;
4.本地域名服务器向顶级域名服务器 .com TLD 进行查询;
5…com TLD 服务器告诉本地域名服务器,下一步查询 example.com 权威域名服务器的 IP 地址;
6.本地域名服务器向 example.com 权威域名服务器发送查询;
7.example.com 权威域名服务器告诉本地域名服务器所查询的主机 IP 地址;
8.本地域名服务器最后把查询的IP地址响应给 Web 浏览器。一旦 DNS 查询的 8 个步骤返回了 example.com 的 IP 地址,浏览器就能够发出对网页的请求;
9.浏览器向 IP 地址发出 HTTP 请求;
10.该 IP 处的 Web 服务器返回要在浏览器中呈现的网页。

名词解释:

1.DNS Resolve: 指本地域名服务器,它是 DNS 查找中的第一站,是负责处理发出初始请求的 DNS 服务器。运营商 ISP 分配的 DNS、谷歌 8.8.8.8 等都属于 DNS Resolver;
2.Root Server:指根域名服务器,当本地域名服务器在本地查询不到解析结果时,则第一步会向它进行查询,并获取顶级域名服务器的 IP 地址;
3.递归查询:是指 DNS 服务器在收到用户发起的请求时,必须向用户返回一个准确的查询结果。如果 DNS 服务器本地没有存储与之对应的信息,则该服务器需要询问其他服务器,并将返回的查询结构提交给用户;
4.迭代查询:是指 DNS 服务器在收到用户发起的请求时,并不直接回复查询结果,而是告诉另一台 DNS 服务器的地址,用户再向这台 DNS 服务器提交请求,这样依次反复,直到返回查询结果。

总结

以上四种异常是我们在编写代码初期经常遇到的问题,通过对以上四种异常的分析,我们可以得到一些经验总结,在后续的开发中,我们可以根据这些总结,进行改进,以便更好的解决问题。

Flutter 常见异常分析相关推荐

  1. Selenium常见异常分析及解决方案

    Selenium常见异常分析及解决方案 参考文章: (1)Selenium常见异常分析及解决方案 (2)https://www.cnblogs.com/superhin/p/11454861.html ...

  2. 常见异常的原因分析和解决方法

    一.ClassCastException ClassCastException,翻译过来就是类型转换错误,通常是进行强制类型转换时候出的错误. 那么这种异常是如何产生的呢?下面我们通过一个例子了解一下 ...

  3. C++软件异常的常见原因分析与总结(实战经验分享)

    目录 1.概述 2.引发软件异常的常见原因 2.1.变量未初始化 2.2.死循环 2.3.内存越界 2.4.内存泄漏 2.5.空指针与野指针 2.6.内存访问违例 2.7.栈内存被当成堆内存去释放 2 ...

  4. Java常见异常类型及原因分析

    Java常见异常类型及原因分析 0x1 NullPointerException异常 顾名思义,NullPointerException 是空指针异常.但是在 Java 中没有指针,怎么会有 空指针异 ...

  5. java.lang.ArrayIndexOutOfBoundsException异常分析及解决

    2019独角兽企业重金招聘Python工程师标准>>> java.lang.ArrayIndexOutOfBoundsException异常分析及解决 这是一个非常常见的异常,从名字 ...

  6. Crash常见异常总结

    平时经常要分析一些Crash问题,将常见的Crash异常总结如下: Crash常见异常 Crash原因 java.lang.NullPointerException 空指针异常 java.lang.A ...

  7. Android ExceptionThrowable 常见异常和解决方法 奔溃日志上报 monkey异常修改

    java将所有的错误封装为一个对象,其根本父类为Throwable, Throwable有两个子类:Error和Exception. 注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理. ...

  8. TCP、UDP、TCP三次握手与四次挥手、TCP如何保证可靠传输、TCP异常分析、拆包和粘包等

    4.OSI模型 4.1.OSI七层模型 4.2.七层模型功能 ​ 物理层:利用传输介质为数据链路层提供物理连接,实现比特流的透明传输,如网线:网卡标准. ​ 数据链路层:接收来自物理层的位流形式的数据 ...

  9. 计算机网络-tcp连接常见异常

    计算机网络-tcp连接常见异常 目标端口未在监听:(操作系统参与) 解决:TCP的服务端实际上就是从网卡的寄存器中读取数据,然后进行解析.对于TCP自然会解析出目的端口这个关键信息,然后根据这个信息查 ...

最新文章

  1. 蝴蝶扇了一下翅膀,混沌就诞生了
  2. java jar 打包命令行_Java程序命令行打包Jar
  3. 自定义安装python,退格,方向键无法正常使用(转)
  4. c# 操作excle
  5. [Redux/Mobx] Redux怎样重置状态?
  6. 传感器工作原理_光电式速度传感器的工作原理
  7. 创建font_使用CSS3 CubicBezier创建动画链接悬停效果
  8. Windows 8 Directx 开发学习笔记(三)摄像机设置及控制正方体旋转
  9. python中处理命令行参数命令getopt
  10. python调用指定浏览器打开网页
  11. 微信小程序商城项目实战(第三篇:商品列表)
  12. 代码动态改变view的大小
  13. SLAM常见面试题集锦
  14. 【论文制图】chemdraw安装与使用
  15. 图文讲解Python数据可视化神器
  16. 如何打开和修改CAJ格式的文档?
  17. 详解word2vec
  18. 计算机软件编程与嵌入式软件编程区别,简单看懂什么是单片机编程与嵌入式系统...
  19. 关于未来计算机的英语作文,《 未来机器人》英语作文
  20. 最大化 LoRa 长距离,您应该知道的完整秘籍

热门文章

  1. 创业之路 - 为什么优秀的人,都把闹钟定在早晨5 : 57 ?
  2. React Native 三端同构实战
  3. 网页设计成品DW静态网页Html5响应式css3 ——响应式游戏网站(24页) HTML+CSS+JavaScript 关于制作网游网页主题论述
  4. mysql 遍历 父子_mysql实现父子递归查询sql
  5. IT企业法律风险防范 -----你准备好了吗?
  6. 什么是 CORS?它是如何工作的 ?
  7. videojs进度条禁用
  8. DIY掌上POS机,或许是最小的收银POS机了!
  9. Cvbox2D 角度 长宽正解
  10. pytorch 随机抽样