PS:自律实践起来真的没有想象那么简单。
Flutter 同系列文章如下:
- Flutter系列之Navigator使用详解
- Flutter系列之Flex布局详解
- Flutter系列之图片加载详解
- Flutter系列之Widget生命周期
- Flutter系列之混合开发Android篇
- Flutter系列之Platform Channel使用详解
Flutter 支持加载的图片类型:JPEG、PNG、GIF、WebP、BMP 和 WBMP,Flutter Image 的组件的必须参数是一个 ImageProvider ,ImageProvider 是一个抽象类,具体获取图片由子类实现,本文将从如下几个方面学习 Flutter 中的图片加载:
- 图片加载
- 图片预加载
- 图片缓存
- 清除图片缓存
- 图片加载进度监听
- 加载图片案例
图片加载
Flutter 本身实现了图片加载,可以加载网络、SD卡、Asset、内存里面的图片,可以通过如下方式生成图片资源对应的 Image:
Image.network(String src,{
...});
Image.file(File file,{
...});
Image.asset(String name,{
...});
Image.memeory(Uint8List bytes,{
...});
下面以加载网络图片为例介绍 Flutter 中图片加载的流程,查看 Image.network() 源代码如下:
Image.network(
// ...
}) : image = NetworkImage(src, scale: scale, headers: headers),
assert(alignment != null),
assert(repeat != null),
assert(matchTextDirection != null),
super(key: key);
使用 Image.network 生成 Image 的时候创建了 NetworkImage,NetworkImage 类是 ImageProvider 的子类,ImageProvider 是一个抽象类,里面提供了解析图片资源的 resolve 方法、将图片缓存移除去的 evict 方法以及加载图片的抽象方法 load 等,load 方法由子类具体实现,ImageProvider 源码分析如下:
/// ImageProvider是一个抽象类,具体加载由子类实现
abstract class ImageProvider<T> {
const ImageProvider();
/// 使用提供的ImageConfiguration对象生成ImageStream
ImageStream resolve(ImageConfiguration configuration) {
assert(configuration != null);
final ImageStream stream = ImageStream();
T obtainedKey;
//...代码
dangerZone.runGuarded(() {
Future<T> key;
try {
// 获取图片资源对应的key
key = obtainKey(configuration);
} catch (error, stackTrace) {
handleError(error, stackTrace);
return;
}
key.then<void>((T key) {
// 获取到图片资源对应的key
obtainedKey = key;
// 获取key对应的ImageStreamCompleter,如果缓存中没有则调用传入的loader回调
// 去加载并将其添加到缓存中
final ImageStreamCompleter completer = PaintingBinding
.instance.imageCache
.putIfAbsent(key, () => load(key), onError: handleError);
if (completer != null) {
stream.setCompleter(completer);
}
}).catchError(handleError);
});
return stream;
}
/// 将图片从缓存中移除,返回值为true表示移除成功
Future<bool> evict(
{
ImageCache cache,
ImageConfiguration configuration = ImageConfiguration.empty}) async {
cache ??= imageCache;
final T key = await obtainKey(configuration);
return cache.evict(key);
}
/// 获取对应图片资源key,具体由子类实现
Future<T> obtainKey(ImageConfiguration configuration);
/// 根据key加载图片并将其转换为ImageStreamCompleter,具体由子类实现
@protected
ImageStreamCompleter load(T key);
@override
String toString() => '$runtimeType()';
}
在 resolve 方法中解析图片资源使用 PaintingBinding 的单例获取图片缓存 imageCache 并调用 putIfAbsent 方法,里面实现了 LRU 缓存基本逻辑,根据是否有缓存进行处理,如果有缓存则从缓存中获取与之对应的图片资源,反之则调用传入的 loader 进行图片加载并将加载的图片添加到缓存 ImageCache 中。
继续查看最终加载网络图片的 ImageProvider 的实现类 NetworkImage 的 load 方法实现如下:
@override
ImageStreamCompleter load(image_provider.NetworkImage key) {
final StreamController<ImageChunkEvent> chunkEvents = StreamController<ImageChunkEvent>();
// _loadAsync方法
return MultiFrameImageStreamCompleter(
codec: _loadAsync(key, chunkEvents),
chunkEvents: chunkEvents.stream,
scale: key.scale,
informationCollector: () {
return <DiagnosticsNode>[
DiagnosticsProperty<image_provider.ImageProvider>('Image provider', this),
DiagnosticsProperty<image_provider.NetworkImage>('Image key', key),
];
},
);
}
load 方法中调用了 _loadAsync,这也是真正的下载图片的方法,同时要对图片进行解码返回,_loadAsync 方法源码如下:
/// 下载图片并解码图片
Future<ui.Codec> _loadAsync(
NetworkImage key,
StreamController<ImageChunkEvent> chunkEvents,
) async {
try {
assert(key == this);
final Uri resolved = Uri.base.resolve(key.url);
final HttpClientRequest request = await _ht

最低0.47元/天 解锁文章
663

被折叠的 条评论
为什么被折叠?



