关于图片加载,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)(原理说明)