}
key.then((T key) {
obtainedKey = key;
// 缓存的处理逻辑在这里,记为A,下面详细介绍
final ImageStreamCompleter completer = PaintingBinding.instance
.imageCache.putIfAbsent(key, () => load(key), onError: handleError);
if (completer != null) {
stream.setCompleter(completer);
}
}).catchError(handleError);
});
return stream;
}
ImageConfiguration
包含图片和设备的相关信息,如图片的大小、所在的AssetBundle
(只有打到安装包的图片存在)以及当前的设备平台、devicePixelRatio(设备像素比等)。Flutter SDK提供了一个便捷函数createLocalImageConfiguration
来创建ImageConfiguration
对象:
ImageConfiguration createLocalImageConfiguration(BuildContext context, { Size size }) {
return ImageConfiguration(
bundle: DefaultAssetBundle.of(context),
devicePixelRatio: MediaQuery.of(context, nullOk: true)?.devicePixelRatio ?? 1.0,
locale: Localizations.localeOf(context, nullOk: true),
textDirection: Directionality.of(context),
size: size,
platform: defaultTargetPlatform,
);
}
我们可以发现这些信息基本都是通过Context
来获取。
上面代码A处就是处理缓存的主要代码,这里的PaintingBinding.instance.imageCache
是 ImageCache
的一个实例,它是PaintingBinding
的一个属性,而Flutter框架中的PaintingBinding.instance
是一个单例,imageCache
事实上也是一个单例,也就是说图片缓存是全局的,统一由PaintingBinding.instance.imageCache
来管理。
下面我们看看ImageCache
类定义:
const int _kDefaultSize = 1000;
const int _kDefaultSizeBytes = 100 << 20; // 100 MiB
class ImageCache {
// 正在加载中的图片队列
final Map<Object, _PendingImage> _pendingImages = <Object, _PendingImage>{};
// 缓存队列
final Map<Object, _CachedImage> _cache = <Object, _CachedImage>{};
// 缓存数量上限(1000)
int _maximumSize = _kDefaultSize;
// 缓存容量上限 (100 MB)
int _maximumSizeBytes = _kDefaultSizeBytes;
// 缓存上限设置的setter
set maximumSize(int value) {…}
set maximumSizeBytes(int value) {…}
… // 省略部分定义
// 清除所有缓存
void clear() {
// …省略具体实现代码
}
// 清除指定key对应的图片缓存
bool evict(Object key) {
// …省略具体实现代码
}
ImageStreamCompleter putIfAbsent(Object key, ImageStreamCompleter loader(), { ImageErrorListener onError }) {
assert(key != null);
assert(loader != null);
ImageStreamCompleter result = _pendingImages[key]?.completer;
// 图片还未加载成功,直接返回
if (result != null)
return result;
// 有缓存,继续往下走
// 先移除缓存,后再添