));
},
);
if (bytes.lengthInBytes == 0)
throw Exception(‘NetworkImage is an empty file: $resolved’);
// 对图片数据进行解码
return PaintingBinding.instance.instantiateImageCodec(bytes);
} finally {
chunkEvents.close();
}
}
可以看到_loadAsync
方法主要做了两件事:
- 下载图片。
- 对下载的图片数据进行解码。
下载逻辑比较简单:通过HttpClient
从网上下载图片,另外下载请求会设置一些自定义的header,开发者可以通过NetworkImage
的headers
命名参数来传递。
在图片下载完成后调用了PaintingBinding.instance.instantiateImageCodec(bytes)
对图片进行解码,值得注意的是instantiateImageCodec(...)
也是一个Native API的包装,实际上会调用Flutter engine的instantiateImageCodec
方法,源码如下:
String _instantiateImageCodec(Uint8List list, _Callback callback, _ImageInfo imageInfo, int targetWidth, int targetHeight)
native ‘instantiateImageCodec’;
obtainKey(ImageConfiguration)
方法
该接口主要是为了配合实现图片缓存,ImageProvider
从数据源加载完数据后,会在全局的ImageCache
中缓存图片数据,而图片数据缓存是一个Map,而Map的key便是调用此方法的返回值,不同的key代表不同的图片数据缓存。
resolve(ImageConfiguration)
方法
resolve
方法是ImageProvider
的暴露的给Image
的主入口方法,它接受一个ImageConfiguration
参数,返回ImageStream
,即图片数据流。我们重点看一下resolve
执行流程:
ImageStream resolve(ImageConfiguration configuration) {
… //省略无关代码
final ImageStream stream = ImageStream();
T obtainedKey; //
//定义错误处理函数
Future handleError(dynamic exception, StackTrace stack) async {
… //省略无关代码
stream.setCompleter(imageCompleter);
imageCompleter.setError(…);
}
// 创建一个新Zone,主要是为了当发生错误时不会干扰MainZone
final Zone dangerZone = Zone.current.fork(…);
dangerZone.runGuarded(() {
Future key;
// 先验证是否已经有缓存
try {
// 生成缓存key,后面会根据此key来检测是否有缓存
key = obtainKey(configuration);
} catch (error, stackTrace) {
handleError(error, stackTrace);
return;
}
key.then((T key) {
obtainedKey = key;
// 缓存的处理逻辑在这里,记为A,下面详细介绍
final ImageStreamCompleter completer = PaintingBinding.instance
.imageCache.putIfAbsent(key, ()