ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
关于图片加载,Flutter本身提供的Image Widget已经实现了加载网络图片的功能,且具备内存缓存的机制,我们看看Image的网络图片加载的实现。 ## Image的构造 ### 有参构造函数 ~~~ Image(Key key, @required this.image, …) ~~~ 开发者可根据自定义的ImageProvider来创建Image。 ### 命名构造函数 ~~~ Image.network(String src, …) ~~~ src即是根据网络获取的图片url地址。 ~~~ Image.file(File file, …) ~~~ file指本地一个图片文件对象,安卓中需要android.permission.READ_EXTERNAL_STORAGE权限。 ~~~ Image.asset(String name, …) ~~~ name指项目中添加的图片资源名,事先在pubspec.yaml文件中有声明。 ~~~ Image.memory(Uint8List bytes, …) ~~~ bytes指内存中的图片数据,将其转化为图片对象。 ## ImageProvider `ImageProvider`一个抽象类,意思是图片的提供者,主要是为Image控件提供图片加载的逻辑,Image的有参构造函数里的image就是需要ImageProvider。 它的子类包括`NetworkImage`、`FileImage`、`ExactAssetImage`、`AssetImage`、`MemoryImage`等,网络加载图片使用的就是`NetworkImage`。 ## Image.network源码分析 Image作为一个StatefulWidget其状态由_ImageState控制,_ImageState继承自State类,其生命周期方法包括initState()、didChangeDependencies()、build()、deactivate()、dispose()、didUpdateWidget()等。我们重点来_ImageState中函数的执行 ### _ImageState源码分析 ~~~ @override void didChangeDependencies() { _updateInvertColors(); _resolveImage(); if (TickerMode.of(context)) _listenToStream(); else _stopListeningToStream(); super.didChangeDependencies(); } ~~~ #### _resolveImage `_updateInvertColors`就是设置了_invertColors的值,重点看看`_resolveImage` ~~~ void _resolveImage() { final ImageStream newStream = widget.image.resolve(createLocalImageConfiguration( context, size: widget.width != null && widget.height != null ? Size(widget.width, widget.height) : null, )); _updateSourceStream(newStream); } void _updateSourceStream(ImageStream newStream) { if (_imageStream?.key == newStream?.key) return; if (_isListeningToStream) _imageStream.removeListener(_getListener()); if (!widget.gaplessPlayback) setState(() { _imageInfo = null; }); setState(() { _loadingProgress = null; _frameNumber = null; _wasSynchronouslyLoaded = false; }); _imageStream = newStream; if (_isListeningToStream) _imageStream.addListener(_getListener()); } ~~~ 创建了一个`ImageStream`对象,该对象是一个图片资源的句柄,其持有着图片资源加载完毕后的监听回调和图片资源的管理者。 由于刚初始化_isListeningToStream为false,因此_updateSourceStream做的事情主要是把_imageStream设置成新创建的ImageStream。 #### _listenToStream 接着分析didChangeDependencies,后面会判断TickerMode.of(context)的值,这个值默认是true,和AnimationConrol有关,后续可以深入研究。然后调用\_listenToStream()。 ~~~ void _listenToStream() { if (_isListeningToStream) return; _imageStream.addListener(_getListener()); _isListeningToStream = true; } ImageStreamListener _getListener([ImageLoadingBuilder loadingBuilder]) { loadingBuilder ??= widget.loadingBuilder; return ImageStreamListener( _handleImageFrame, onChunk: loadingBuilder == null ? null : _handleImageChunk, ); } void _handleImageFrame(ImageInfo imageInfo, bool synchronousCall) { setState(() { _imageInfo = imageInfo; _loadingProgress = null; _frameNumber = _frameNumber == null ? 0 : _frameNumber + 1; _wasSynchronouslyLoaded |= synchronousCall; }); } ~~~ 这里主要是向ImageStream对象中添加了监听器\_handleImageChanged,监听方法如下监听方法最终回调用`setState`方法来通知界面刷新。 ### ImageProvider源码分析 `ImageStream`对象是通过`widget.image.resolve`方法创建的,也就是对应`NetworkImage`的`resolve`方法,实现在`ImageProvider`类中找到了。 #### resolve ~~~ ImageStream resolve(ImageConfiguration configuration) { assert(configuration != null); final ImageStream stream = ImageStream(); T obtainedKey; final Zone dangerZone = Zone.current.fork( specification: ZoneSpecification( handleUncaughtError: (Zone zone, ZoneDelegate delegate, Zone parent, Object error, StackTrace stackTrace) { handleError(error, stackTrace); } ) ); dangerZone.runGuarded(() { Future<T> key; try { key = obtainKey(configuration); } catch (error, stackTrace) { handleError(error, stackTrace); return; } key.then<void>((T key) { obtainedKey = key; final ImageStreamCompleter completer = PaintingBinding.instance .imageCache.putIfAbsent(key, () => load(key), onError: handleError); if (completer != null) { stream.setCompleter(completer); } }).catchError(handleError); }); return stream; } ~~~ `ImageStreamCompleter`对象就是图片资源的一个管理类。 ImageStream中的图片管理者ImageStreamCompleter通过`PaintingBinding.instance.imageCache.putIfAbsent(key, () => load(key), onError: handleError);`方法创建,imageCache是Flutter框架中实现的用于图片缓存的单例,查看其中的`putIfAbsent`方法 。 ## QA ### ImageProvider是啥 ## 参考资料 [Flutter Image全解析](https://www.jianshu.com/p/68879dd00f81)(Image详细参数说明) [Flutter中网络图片加载和缓存](https://blog.csdn.net/weixin_43499085/article/details/88842438)(原理说明)