Flutter 异常处理之图片篇
背景
說到異常處理,你可能直接會認為不就是 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
最后來一個彩蛋表情包:
轉載于:https://www.cnblogs.com/nesger/p/10709079.html
總結
以上是生活随笔為你收集整理的Flutter 异常处理之图片篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pycharm 进行远程服务器修改与调试
- 下一篇: 树上分块初步