Flutter系列之图片加载详解

PS:自律实践起来真的没有想象那么简单。

Flutter 同系列文章如下:

Flutter 支持加载的图片类型:JPEG、PNG、GIF、WebP、BMP 和 WBMP,Flutter Image 的组件的必须参数是一个 ImageProviderImageProvider 是一个抽象类,具体获取图片由子类实现,本文将从如下几个方面学习 Flutter 中的图片加载:

  1. 图片加载
  2. 图片预加载
  3. 图片缓存
  4. 清除图片缓存
  5. 图片加载进度监听
  6. 加载图片案例
图片加载

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 的时候创建了 NetworkImageNetworkImage 类是 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 的实现类 NetworkImageload 方法实现如下:

@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
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

躬行之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值