Flutter的 Image Widget
源码:image.dart
图片的显示
class Image extends StatefulWidget
Image
继承自 [StatefulWidget
],它是具有状态的,通过
@override///image.dart 574L
_ImageState createState() => _ImageState();
可以找到 Image
对应的State
类是 _ImageState
, 那么构建Widget的方法就在_ImageState
的build
方法中,如下:
@override
Widget build(BuildContext context) {
final RawImage image = RawImage(
image: _imageInfo?.image,
width: widget.width,
height: widget.height,
scale: _imageInfo?.scale ?? 1.0,
color: widget.color,
colorBlendMode: widget.colorBlendMode,
fit: widget.fit,
alignment: widget.alignment,
repeat: widget.repeat,
centerSlice: widget.centerSlice,
matchTextDirection: widget.matchTextDirection,
invertColors: _invertColors,
filterQuality: widget.filterQuality,
);
if (widget.excludeFromSemantics)
return image;
return Semantics(
container: widget.semanticLabel != null,
image: true,
label: widget.semanticLabel == null ? '' : widget.semanticLabel,
child: image,
);
}
由源码可以看到,在此方法中创建的是 RawImage
widget ,传入 imageInfo.image
,并由 RawImage
来渲染图片数据。
图片的加载
Image
类有这么几个构造方法,方便开发者加载显示本地,文件,网络中的图片数据。
/// * [new Image], for obtaining an image from an [ImageProvider].
/// * [new Image.asset], for obtaining an image from an [AssetBundle]
/// using a key.
/// * [new Image.network], for obtaining an image from a URL.
/// * [new Image.file], for obtaining an image from a [File].
/// * [new Image.memory], for obtaining an image from a [Uint8List].
Image
加载了图片数据后存储在 imageInfo.image
。ImageInfo
类很简单,只有两个属性:
@immutable
class ImageInfo {
//图片像素点阵,
final ui.Image image;
//缩放比例,例当scale为2时,宽高都将变为原图的2倍。
final double scale;
}
那imageInfo
又是在哪生成的呢,也就是在哪加载了图片的数据呢?根据 ImageInfo
类的注释, ImageInfo
一旦被获得,就会被 ImageStream
用来表示为真实的图片数据。
ImageStream
的部分源码如下:
class ImageStream extends Diagnosticable {
/// Create an initially unbound image stream.
///
/// Once an [ImageStreamCompleter] is available, call [setCompleter].
ImageStream();
/// The completer that has been assigned to this image stream.
///
/// Generally there is no need to deal with the completer directly.
ImageStreamCompleter get completer => _completer;
ImageStreamCompleter _completer;
可见 ImageStream
主要是由 ImageStreamCompleter
来提供支持,只是一个 ImageStreamCompleter
的包装类,不过当ImageStreamCompleter
可用的时候,需调用ImageStream.setCompleter
方法,以将事件传递给ImageStream
中的监听者。
那么 ImageStreamCompleter
又是个啥?继续往下看,源码:
/// Base class for those that manage the loading of [dart:ui.Image] objects for
/// [ImageStream]s.
///
/// [ImageStreamListener] objects are rarely constructed directly. Generally, an
/// [ImageProvider] subclass will return an [ImageStream] and automatically
/// configure it with the right [ImageStreamCompleter] when possible.
abstract class ImageStreamCompleter extends Diagnosticable {
final List<_ImageListenerPair> _listeners = <_ImageListenerPair>[];
ImageInfo _currentImage;
void addListener(ImageListener listener, { ImageErrorListener onError }) {
//省略
}
void removeListener(ImageListener listener) {
//省略
}
/// Calls all the registered listeners to notify them of a new image.
@protected
void setImage(ImageInfo image) {
_currentImage = image;
if (_listeners.isEmpty)
return;
final List<ImageListener> localListeners = _listeners.map<ImageListener>(
(_ImageListenerPair listenerPair) => listenerPair.listener
).toList();
for (ImageListener listener in localListeners) {
try {
listener(image, false);
} catch (exception, stack) {
reportError(
context: 'by an image listener',
exception: exception,
stack: stack,
);
}
}
}
}
ImageStreamCompleter
是一个抽象类。去掉添加/移除Listener的方法后,还剩一个 [setImage
] 方法,方法内部逻辑很简单,将传入的参数 ImageInfo
传递到各个 ImageListener
,然后刷新GUI。
ImageStreamCompleter
有两个实现类,分别为
-
OneFrameImageStreamCompleter
-
MultiFrameImageStreamCompleter
。
那刚才看下来的源码只是一个监听的设计而已:
widget
监听 ImageStream
, 而widget
设置给ImageStream
的 listener
被传递到 ImageStreamCompleter
。当图片成功加载时,ImageStreamCompleter
的setImage
方法被调用,图片通过回调回传到 widget
。
图片的加载2
Image类构造方法需传入一个 ImageProvider
,图片应便是在这里面被加载的:
abstract class ImageProvider<T> {
///根据 configuration 处理 ImagePrivoder,并返回一个 ImageStream对象
///子类应该实现 [obtainKey] 和 [load] 方法,并且这两个方法在此流程中使用
ImageStream resolve(ImageConfiguration configuration) {
assert(configuration != null);
final ImageStream stream = ImageStream();
T obtainedKey;
obtainKey(configuration).then<void>((T key) {
obtainedKey = key;
///当拿到KEY时,查询缓存
stream.setCompleter(PaintingBinding.instance.imageCache.putIfAbsent(key, () => load(key)));
}).catchError(
//省略错误处理...
return null;
}
);
return stream;
}
/// 根据 ImageConfiguration和 ImageProvider 的属性来生成一个KEY用来标识加载的图片
/// KEY要求实现 '==' 和 hascode 方法,这个KEY主要是用于缓存
@protected
Future<T> obtainKey(ImageConfiguration configuration);
///开始加载图片
@protected
ImageStreamCompleter load(T key);
}
其中 resolve
方法根据ImageConfiguration
来获取相应的ImageStream
。
到目前为止,图片获取的流程应该差不多可以这样来表示...
image.png图片的缓存
在ImageProvider
的源码中能过看到,图片的加载是做过缓存处理的。即在ImageProvider
的resolve
方法中,有这么一句:
stream.setCompleter(PaintingBinding.instance.imageCache.putIfAbsent(key, () => load(key)));
1. KEY
image.putIfAbsent
传入的 key 的类型即为ImageProvider<T>
中的T
,需是不可改变的(immutable)以及实现[==
] 和 [hashcode
]方法。并且由 ImageProvider.obtainKey
方法生成,例如NetworkImage
中是这么实现的:
class NetworkImage extends ImageProvider<NetworkImage> {
@override
Future<NetworkImage> obtainKey(ImageConfiguration configuration) {
return SynchronousFuture<NetworkImage>(this);
}
@override
bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType)
return false;
final NetworkImage typedOther = other;
return url == typedOther.url
&& scale == typedOther.scale;
}
@override
int get hashCode => hashValues(url, scale);
}
2.ImageCache
缓存的逻辑主要在 putIfAbent
方法中
使用的 LRU,并且默认最多存储 1000个缓存,最大缓存限制为100MiB
const int _kDefaultSize = 1000;
const int _kDefaultSizeBytes = 100 << 20; // 100 MiB
目前的图片大小是这么计算的:
final int imageSize = info?.image == null ? 0 : info.image.height * info.image.width * 4;
3.缓存
由上代码可以看出,flutter 自带的缓存只会在运行期间生效,也就是缓存在内存中。