本文主要介绍探索Flutter_Image显示Webp逻辑
简介最近探索了一下新增Flutter的Image widget对webp做一个stopAnimation的拓展的Api,顺便了解一下Image整个结构和对一些多帧图片的处理。 我们先看看Image的一个类图结构。
其中:
- ImageProvider 提供加载图片的入口,不同的图片资源加载方式不一样,只要重写其load方法即可。同样,缓存图片的key值也有其生成。
- FileImage 负责读取文件图片的数据,读取到的文件数据转化成ui.Codec对象交给ImageStreamCompleter去处理解析。
- ImageStreamCompleter就是逐帧解析图片的类,生成之后会加入ImageCache,下载可以从缓存中得到。
- ImageStream是处理Image Resource的,ImageState通过ImageStream与ImageStreamCompleter建立联系。ImageStream里也存储着图片加载完毕的监听回调。
- MultiFrameImageStreamCompleter就是多帧图片解析器。 Flutter imgae支持的图片格式为:JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, and WBMP。Flutter Image是显示图片的一个Widget。 Flutter Image的几个构造方法:
从Image的构造体上看,ImageProvider才是图片提供方,所以我们后面会看看ImageProvider究竟是要做点什么的。 其他的参数是一些图片的属性和一些builder。
ImageState关键代码:
void didUpdateWidget(Image oldWidget) {
super.didUpdateWidget(oldWidget);
if (_isListeningToStream &&
(widget.loadingBuilder == null) != (oldWidget.loadingBuilder == null)) {
_imageStream.removeListener(_getListener(oldWidget.loadingBuilder));
_imageStream.addListener(_getListener());
}
if (widget.image != oldWidget.image)
_resolveImage();
}
ImageProvider
其实ImageProvider是一个抽象类,让需要定制的子类去做一些实现。
比如:FileImage、MemoryImage、ExactAssetImage等等。其中对FileImage的代码进行了一些分析。
class FileImage extends ImageProvider {
/// Creates an object that decodes a [File] as an image.
///
/// The arguments must not be null.
const FileImage(this.file, { this.scale: 1.0 })
: assert(file != null),
assert(scale != null);
/// The file to decode into an image.
final File file;
/// The scale to place in the [ImageInfo] object of the image.
final double scale;
@override
Future obtainKey(ImageConfiguration configuration) {
return new SynchronousFuture(this);
}
@override
ImageStreamCompleter load(FileImage key) {
return new MultiFrameImageStreamCompleter(
codec: _loadAsync(key),
scale: key.scale,
informationCollector: (StringBuffer information) {
information.writeln('Path: ${file?.path}');
}
);
}
Future _loadAsync(FileImage key) async {
assert(key == this);
final Uint8List bytes = await file.readAsBytes();
if (bytes.lengthInBytes == 0)
return null;
return await ui.instantiateImageCodec(bytes);
}
@override
bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType)
return false;
final FileImage typedOther = other;
return file?.path == typedOther.file?.path
&& scale == typedOther.scale;
}
@override
int get hashCode => hashValues(file?.path, scale);
@override
String toString() => '$runtimeType("${file?.path}", scale: $scale)';
}
FileImage重写了 obtainKey、load的方法。但是在什么地方会调用这两个重写的方法呢?那肯定是ImageProvider这个父类了。
@optionalTypeArgs
abstract class ImageProvider {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const ImageProvider();
/// Resolves this image provider using the given `configuration`, returning
/// an [ImageStream].
///
/// This is the public entry-point of the [ImageProvider] class hierarchy.
///
/// Subclasses should implement [obtainKey] and [load], which are used by this
/// method.
ImageStream resolve(ImageConfiguration configuration) {
assert(configuration != null);
final ImageStream stream = new ImageStream();
T obtainedKey;
obtainKey(configuration).then((T key) {
obtainedKey = key;
stream.setCompleter(PaintingBinding.instance.imageCache.putIfAbsent(key, () => load(key)));
}).catchError(
(dynamic exception, StackTrace stack) async {
FlutterError.reportError(new FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: 'while resolving an image',
silent: true, // could be a network error or whatnot
informationCollector: (StringBuffer information) {
information.writeln('Image provider: $this');
information.writeln('Image configuration: $configuration');
if (obtainedKey != null)
information.writeln('Image key: $obtainedKey');
}
));
return null;
}
);
return stream;
}
/// Converts an ImageProvider's settings plus an ImageConfiguration to a key
/// that describes the precise image to load.
///
/// The type of the key is determined by the subclass. It is a value that
/// unambiguously identifies the image (_including its scale_) that the [load]
/// method will fetch. Different [ImageProvider]s given the same constructor
/// arguments and [ImageConfiguration] objects should return keys that are
/// '==' to each other (possibly by using a class for the key that itself
/// implements [==]).
@protected
Future obtainKey(ImageConfiguration configuration);
/// Converts a key into an [ImageStreamCompleter], and begins fetching the
/// image.
@protected
ImageStreamCompleter load(T key);
@override
String toString() => '$runtimeType()';
}
该方法的作用就是创建一个ImageStream,并且ImageConfiguration作为key从ImageCache中获取ImageCompleter,设置到ImageStream上面。而ImageCompleter是为了设置一些回调和帮助ImageStream设置图片的一个类。
ImageConfiguration是对于ImageCompleter的一些配置。
ImageCache是对于ImageCompleter的缓存。 ImageStreamCompleter putIfAbsent(Object key, ImageStreamCompleter loader(), { ImageErrorListener onError }) 这个方法在resolve方法中是一个关键方法。
ImageStreamCompleter putIfAbsentImageStreamCompleter putIfAbsent(Object key, ImageStreamCompleter loader()) {
assert(key != null);
assert(loader != null);
ImageStreamCompleter result = _cache[key];
if (result != null) {
// Remove the provider from the list so that we can put it back in below
// and thus move it to the end of the list.
_cache.remove(key);
} else {
if (_cache.length == maximumSize && maximumSize > 0)
_cache.remove(_cache.keys.first);
result = loader();
}
if (maximumSize > 0) {
assert(_cache.length < maximumSize);
_cache[key] = result;
}
assert(_cache.length
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?