背景

说到异常处理,你可能直接会认为不就是 try-catch 的事情,至于写一篇文章单独来说明吗?

如果你是这么想的,那么本篇说不定会给你惊喜哦~

而且本篇聚焦在图片的异常处理。

场景

学以致用,有具体的应用场景,能够加深我们对知识的掌握。

我们以简书的文章列表为例,如下图:

假设产品有这样的需求,当右边的封面图加载失败的时候,用一个默认图片替换或者直接让文本横向填充原有图片位置。

不管处理方式是怎样,首先我们要做的就是能够知道图片加载失败。

如何获知图片加载失败呢?下面我们通过 Flutter 自带网络加载 API 和一个第三方网络库来进行对比说明。

Image.network

我们看下源码,如下:

Image.network(String src, {
Key key,
double scale = 1.0,
this.semanticLabel,
this.excludeFromSemantics = false,
this.width,
this.height,
this.color,
this.colorBlendMode,
this.fit,
this.alignment = Alignment.center,
this.repeat = ImageRepeat.noRepeat,
this.centerSlice,
this.matchTextDirection = false,
this.gaplessPlayback = false,
this.filterQuality = FilterQuality.low,
Map<String, String> headers,
}) : image = NetworkImage(src, scale: scale, headers: headers),
assert(alignment != null),
assert(repeat != null),
assert(matchTextDirection != null),
super(key: key);

可以看到只有 src 是必填参数,因此我们给出 src 为不同值的情况。

1.一个图片的 url

  Widget _buildWidget() {
return Image.network('https://upload-images.jianshu.io/upload_images/5361063-e413832da0038304.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/800');
}

能够正常显示如下图:

2.不可访问 url,如随便一个字符串 test

  Widget _buildWidget() {
return Image.network('test');
}

终端报错如下:

flutter: ══╡ EXCEPTION CAUGHT BY IMAGE RESOURCE SERVICE ╞════════════════════════════════════════════════════
flutter: The following ArgumentError was thrown resolving an image codec:
flutter: Invalid argument(s): No host specified in URI file:///test

模拟器显示空白。

这种场景假设我们要捕获异常,增加 try-catch,如下:

  Widget _buildWidget() {
try {
return Image.network('test');
} catch (e) {
print('enter catch exception start');
print(e);
print('enter catch exception end');
return Container();
}
}

依然没法捕获

3.可访问非图片 url,比如 http://mp.weixin.qq.com/mp/homepage?__biz=MzI3OTAyNzAwNg==&hid=5&sn=7e4598d8b00537fe2846f2e85d746b9a&scene=18#wechat_redirect

  Widget _buildWidget() {
try {
return Image.network('http://mp.weixin.qq.com/mp/homepage?__biz=MzI3OTAyNzAwNg==&hid=5&sn=7e4598d8b00537fe2846f2e85d746b9a&scene=18#wechat_redirect');
} catch (e) {
print('enter catch exception start');
print(e);
print('enter catch exception end');
return Container();
}
}

控制台抛出如下异常

[VERBOSE-2:codec.cc(97)] Failed decoding image. Data is either invalid, or it is encoded using an unsupported format.
flutter: ══╡ EXCEPTION CAUGHT BY IMAGE RESOURCE SERVICE ╞════════════════════════════════════════════════════
flutter: The following _Exception was thrown resolving an image codec:
flutter: Exception: operation failed

可以看到 try-catch 一样没法生效。没有打印相关日志。

cached_network_image

这是一个第三方开发的网络库,pub 地址为 https://pub.dartlang.org/packages/cached_network_image

因为项目有用到这个库,所以用这个来举例,并不是为其打广告,至于你实际开发是否用这个库,还是有其他更好的库,需要你自己去评估。

因为这个是项目组 iOS 同事选择的,我这边并没有深入研究过。

我们仿照上面的依次执行 3 种 case。

1.一个图片的 url

  Widget _buildWidget() {
return Image(image: new CachedNetworkImageProvider('https://upload-images.jianshu.io/upload_images/5361063-e413832da0038304.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/800'));
}

能够正常显示如下图:

2.不可访问 url,如随便一个字符串 test

  Widget _buildWidget() {
return Image(image: new CachedNetworkImageProvider('test'));
}

终端报错如下:

flutter: ══╡ EXCEPTION CAUGHT BY IMAGE RESOURCE SERVICE ╞════════════════════════════════════════════════════
flutter: The following message was thrown resolving an image codec:
flutter: Couldn't download or retrieve file.

模拟器显示空白。

这种场景假设我们要捕获异常,增加 try-catch,如下:

  Widget _buildWidget() {
try {
return Image(image: new CachedNetworkImageProvider('test'));
} catch (e) {
print('enter catch exception start');
print(e);
print('enter catch exception end');
return Container();
}
}

依然没法捕获。

但是我们通过其自带的错误回调,如下:

  Widget _buildWidget() {
return Image(
image: new CachedNetworkImageProvider(
'test',
errorListener: () {
print('enter errorListener');
}
)
);
}

可以看到控制台进入了 errorListener,打印了对应日志。

虽然 Flutter 自带的错误日志依然输出了,但是通过 errorListener 我们可以获得这种异常情况。

flutter: enter errorListener
flutter: ══╡ EXCEPTION CAUGHT BY IMAGE RESOURCE SERVICE ╞════════════════════════════════════════════════════
flutter: The following message was thrown resolving an image codec:
flutter: Couldn't download or retrieve file.

3.可访问非图片 url,比如 http://mp.weixin.qq.com/mp/homepage?__biz=MzI3OTAyNzAwNg==&hid=5&sn=7e4598d8b00537fe2846f2e85d746b9a&scene=18#wechat_redirect

  Widget _buildWidget() {
return Image(
image: new CachedNetworkImageProvider(
'http://mp.weixin.qq.com/mp/homepage?__biz=MzI3OTAyNzAwNg==&hid=5&sn=7e4598d8b00537fe2846f2e85d746b9a&scene=18#wechat_redirect',
errorListener: () {
print('enter errorListener');
}
)
);
}

运行,控制台会报错,并且没法捕获,控制台输出如下:

[VERBOSE-2:codec.cc(97)] Failed decoding image. Data is either invalid, or it is encoded using an unsupported format.
flutter: ══╡ EXCEPTION CAUGHT BY IMAGE RESOURCE SERVICE ╞════════════════════════════════════════════════════
flutter: The following _Exception was thrown resolving an image codec:
flutter: Exception: operation failed

使用 try-catch 也是一样,这里就不赘余了。

图片通用异常捕获处理

通过上面的学习,我们可以发现不管是 Image.network 还是 cached_network_image 没法覆盖全上面两种异常的捕获处理。

不过这两个的共同点就是他们都返回 Image。

所以对于图片的异常捕获可以使用下面通用模板:

//    Image image = Image(image: new CachedNetworkImageProvider(''));
Image image = Image.network('');
final ImageStream stream = image.image.resolve(ImageConfiguration.empty);
stream.addListener((_, __) {}, onError: (dynamic exception, StackTrace stackTrace) {
//TODO error callback
});

这里首先是获得 Image,如果获得的是 ImageProvider,只需要把 image.image 换为你的 ImageProvider 即可,当然这个笔者没测试,只是看源码上面模板 image.image 的类型是 ImageProvider。

addListener 有两个回调,其中成功回调是必填的,有两个参数,因为这里不需要用到,因此第一个参数是一个下划线,第二个参数是两个下划线。可能你会说不需要用到,可不可以直接填 null。不行,这边测试了,填 null 当图片加载成功时控制台会抛异常。所以提供一个不需要任何实现的回调即可。

错误回调是可选的,因为我们本篇的主题就是要获取错误回调,所以这里提供了实现。

针对我们上面的 3 个例子,我们看看通用模板是否可以全部捕获。

1.一个图片的 url

Widget _buildWidget() {
Image image = Image.network('https://upload-images.jianshu.io/upload_images/5361063-e413832da0038304.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/800');
final ImageStream stream = image.image.resolve(ImageConfiguration.empty);
stream.addListener((_,__){}, onError: (dynamic exception, StackTrace stackTrace) {
print('enter onError start');
print(exception);
print(stackTrace);
print('enter onError end');
});
return image;
}

图片加载成功。

2.不可访问 url,如随便一个字符串 test

Widget _buildWidget() {
Image image = Image.network('test');
final ImageStream stream = image.image.resolve(ImageConfiguration.empty);
stream.addListener((_,__){}, onError: (dynamic exception, StackTrace stackTrace) {
print('enter onError start');
print(exception);
print(stackTrace);
print('enter onError end');
});
return image;
}

控制台输出如下:

flutter: enter onError start
flutter: Invalid argument(s): No host specified in URI file:///test
flutter: #0 _HttpClient._openUrl (dart:_http/http_impl.dart:2121:9)
#1 _HttpClient.getUrl (dart:_http/http_impl.dart:2056:48)
#2 NetworkImage._loadAsync (package:flutter/src/painting/image_provider.dart:486:57)
<asynchronous suspension>
#3 NetworkImage.load (package:flutter/src/painting/image_provider.dart:471:14)
#4 ImageProvider.resolve.<anonymous closure>.<anonymous closure> (package:flutter/src/painting/image_provider.dart:267:86)
#5 ImageCache.putIfAbsent (package:flutter/src/painting/image_cache.dart:143:20)
#6 ImageProvider.resolve.<anonymous closure> (package:flutter/src/painting/image_provider.dart:267:63)
#7 SynchronousFuture.then (package:flutter/src/foundation/synchronous_future.dart:38:29)
#8 ImageProvider.resolve (package:flutter/src/painting/image_provider.dart:265:30)
#9 MyApp._buildWidget (package:my_flutter/main.dart:20:42)
#10 MyApp.build (package:my_flutter/main.dart:12:18)
#11 StatelessElement.build (package:flutter/<…>
flutter: enter onError end

可以看到确实进入错误回调了。

3.可访问非图片 url,比如 http://mp.weixin.qq.com/mp/homepage?__biz=MzI3OTAyNzAwNg==&hid=5&sn=7e4598d8b00537fe2846f2e85d746b9a&scene=18#wechat_redirect

Widget _buildWidget() {
Image image = Image.network('http://mp.weixin.qq.com/mp/homepage?__biz=MzI3OTAyNzAwNg==&hid=5&sn=7e4598d8b00537fe2846f2e85d746b9a&scene=18#wechat_redirect');
final ImageStream stream = image.image.resolve(ImageConfiguration.empty);
stream.addListener((_,__){}, onError: (dynamic exception, StackTrace stackTrace) {
print('enter onError start');
print(exception);
print(stackTrace);
print('enter onError end');
});
return image;
}

控制台输出如下:

[VERBOSE-2:codec.cc(97)] Failed decoding image. Data is either invalid, or it is encoded using an unsupported format.
flutter: enter onError start
flutter: Exception: operation failed
flutter: #0 NetworkImage._loadAsync (package:flutter/src/painting/image_provider.dart:498:12)
<asynchronous suspension>
#1 NetworkImage.load (package:flutter/src/painting/image_provider.dart:471:14)
#2 ImageProvider.resolve.<anonymous closure>.<anonymous closure> (package:flutter/src/painting/image_provider.dart:267:86)
#3 ImageCache.putIfAbsent (package:flutter/src/painting/image_cache.dart:143:20)
#4 ImageProvider.resolve.<anonymous closure> (package:flutter/src/painting/image_provider.dart:267:63)
#5 SynchronousFuture.then (package:flutter/src/foundation/synchronous_future.dart:38:29)
#6 ImageProvider.resolve (package:flutter/src/painting/image_provider.dart:265:30)
#7 MyApp._buildWidget (package:my_flutter/main.dart:20:42)
#8 MyApp.build (package:my_flutter/main.dart:12:18)
#9 StatelessElement.build (package:flutter/src/widgets/framework.dart:3774:28)
#10 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3721:15<…>
flutter: enter onError end

可以看到确实进入错误回调了。

更多阅读:

Flutter 入门系列博客

Flutter & Dart

最后来一个彩蛋表情包:

Flutter 异常处理之图片篇的更多相关文章

  1. UI2Code智能生成Flutter代码--整体设计篇

    摘要: UI2CODE项目是闲鱼技术团队研发的一款通过机器视觉理解+AI人工智能将UI视觉图片转化为端侧代码的工具. 背景: 随着移动互联网时代的到来,人类的科学技术突飞猛进.然而软件工程师们依旧需要 ...

  2. 【Flutter实战】图片组件及四大案例

    老孟导读:大家好,这是[Flutter实战]系列文章的第三篇,这一篇讲解图片组件,Image有很多高级用法,希望对您有所帮助. 图片组件是Flutter基础组件之一,和文本组件一样必不可少.图片组件包 ...

  3. C#多线程编程(4)--异常处理+前三篇的总结

    本来是打算讲并行For和PLINQ的,但是我感觉前三篇我没有讲得很清晰.之前一直在看<CLR via C#>(后文简称CLR)的多线程部分,其中有些部分不是很明白,今天翻开<果壳中的 ...

  4. 2、Flutter 填坑记录篇

    1.前言 之前写了一篇文章关于 flutter 初体验的一篇,https://www.cnblogs.com/niceyoo/p/9240359.html,当时一顿骚操作,然后程序就跑起来了. 隔了好 ...

  5. SpringMVC入门(二)—— 参数的传递、Controller方法返回值、json数据交互、异常处理、图片上传、拦截器

    一.参数的传递 1.简单的参数传递 /* @RequestParam用法:入参名字与方法名参数名不一致时使用{ * value:传入的参数名,required:是否必填,defaultValue:默认 ...

  6. JAVAEE——SpringMVC第二天:高级参数绑定、@RequestMapping、方法返回值、异常处理、图片上传、Json交互、实现RESTful、拦截器

    1. 课前回顾 https://www.cnblogs.com/xieyupeng/p/9093661.html 2. 课程计划 1.高级参数绑定 a) 数组类型的参数绑定 b) List类型的绑定 ...

  7. Flutter Image(图片)

    Image是一个用于展示图片的组件.支持 JPEG.PNG.GIF.Animated GIF.WebP.Animated WebP.BMP 和 WBMP 等格式. Image 有许多的静态函数: ne ...

  8. Flutter生成带图片的二维码

    现在的APP中经常需要用自己的信息生成一个二维码给别人扫,下面就介绍一下Flutter中怎么生成一个带图片的二维码. 需要用到的插件qr_flutter 首先在 pubspec.yaml 文件中添加以 ...

  9. Flutter | 状态管理特别篇——Provide

    前言 今天偶然发现在谷歌爸爸的仓库下出现了一个叫做flutter-provide的状态管理框架,2月8日才第一次提交,非常新鲜.在简单上手之后感觉就是一个字--爽!所以今天就跟大家分享一下这个新的状态 ...

随机推荐

  1. [CVPR2017] Weakly Supervised Cascaded Convolutional Networks论文笔记

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px "Helvetica Neue"; color: #042eee } p. ...

  2. mysql explain extra理解

    Extra表示附加信息,常见的有如下几种(也按查询效率从高到低排列): Using index 表示使用索引,如果只有 Using index,说明他没有查询到数据表,只用索引表就完成了这个查询,这个 ...

  3. Socket TCP/UDP

    TCP TCPClient package com.tcp; import java.io.*; import java.net.*; class TCPClient { public static ...

  4. mysql事务隔离级别详解和实战

    A事务做了操作 没有提交 对B事务来说 就等于没做 获取的都是之前的数据 但是 在A事务中查询的话 查到的都是操作之后的数据 没有提交的数据只有自己看得到,并没有update到数据库. 查看InnoD ...

  5. serialPort操作结构体Hashtable的使用

  6. HTML5 CSS3专题 诱人的实例 CSS3打造百度贴吧的3D翻牌效果

    首先感谢w3cfuns的老师~ 今天给大家带来一个CSS3制作的翻牌效果,就是鼠标移到元素上,感觉可以看到元素背后的信息.大家如果制作考验记忆力的连连看.扑克类的游戏神马的,甚至给女朋友写一些话语,放 ...

  7. CSS布局-圣杯布局

    圣杯布局 圣杯布局很完美(兼容所有浏览器,包括IE6),但是使用了相对定位,布局有局限性,宽度控制要改的地方也多. 第一种方法公用部分: .lgyz, .lzrg, .lrzcg, .lcgrz, . ...

  8. VMWare Workstation虚拟机 安装Centos7 图文指南

    本篇博文将讲述如何一步一步在VMWare Workstation 中安装Centos 7 1. 准备工作 VMWare Workstation Centos7 镜像 VMWare Workstatio ...

  9. 用beego开发服务端应用

    用beego开发服务端应用 说明 Quick Start 安装 创建应用 编译运行 打包发布 代码生成 开发文档 目录结构说明 使用配置文件 beego默认参数 路由设置 路由的表述方式 直接设置路由 ...

  10. Java并发编程(五)锁的使用(下)

    显式锁 上篇讲了使用synchronized关键字来定义锁,其实Java除了使用这个关键字外还可以使用Lock接口及其实现的子类来定义锁,ReentrantLock类是Lock接口的一个实现,Reen ...