Flutter - Image 组件分析

本文详细解析了Flutter中Image组件与ImageProvider的工作原理,特别是NetworkImage的加载流程,包括图片下载、解码、缓存管理和错误处理。重点介绍了ImageStream、ImageStreamCompleter和如何利用key进行图片缓存控制。

        Flutter 中提供了Widget 组件供开发者解决日常中关于图片相关的需求,其中包含 Image.file() 读取用户内存中图片,Image.asset() 加载程序包含的图片资源,Image.network() 用于网络图片的加载,我们主要通过 Image 对于网络图片加载的实现来初步了解 Image 组件的原理。

首先从构造函数开始:

Image.network(
  String src, {
  Key? key,
  double scale = 1.0,
  ...
}) : image = ResizeImage.resizeIfNeeded(cacheWidth, cacheHeight, NetworkImage(src, scale: scale, headers: headers)),
     ...
     super(key: key);
     
/// The image to display.
final ImageProvider image;

        其中在构造Image对象时,最重要的变量 image,是一个ImageProvider 对象,从名字可以看出他是图片的提供者,ImageProvider 是一个抽象类,子类包括 NetworkImage,FileImage,AssetImage,MemoryImage等,稍后我们细说NetworkImage,继续看 Image 组件的实现。

        Image 作为一个 StatefulWidget 他的状态由 _ImageState 控制,从生命周期的执行顺序看下来

@override
void didChangeDependencies() {
  _updateInvertColors();
  _resolveImage();

  if (TickerMode.of(context))
    _listenToStream();
  else
    _stopListeningToStream(keepStreamAlive: true);

  super.didChangeDependencies();
}

void _resolveImage() {
  final ScrollAwareImageProvider provider = ScrollAwareImageProvider<Object>(
    context: _scrollAwareContext,
    imageProvider: widget.image,
  );
  final ImageStream newStream =
    provider.resolve(createLocalImageConfiguration(
      context,
      size: widget.width != null && widget.height != null ? Size(widget.width!, widget.height!) : null,
    ));
  assert(newStream != null);
  _updateSourceStream(newStream);
}

        函数通过调用 imageProvider 的 resolve 方法创建 ImageStream 对象,该对象是一个图片资源的句柄,它持有图片资源加载完毕后的监听回调和图片资源的管理者,而后续介绍到其中的 ImageStreamCompleter 对象是图片资源的一个管理类; resolve 方法是 ImageProvider 暴露给 Image 组件的主入口方法,接受一个图片相关的配置对象,返回对应的 ImageStream 图片数据流,我们深入查看 NetwokImage 的 resolve 实现。

@nonVirtual
ImageStream resolve(ImageConfiguration configuration) {
  assert(configuration != null);
  final ImageStream stream = createStream(configuration);
  // 加载key(可能是异步的),设置错误处理zone,然后调用 resolveStreamForKey。
  _createErrorHandlerAndKey(
    configuration,
    (T key, ImageErrorListener errorHandler) {
      resolveStreamForKey(configuration, stream, key, errorHandler);
    },
    (T? key, Object exception, StackTrace? stack) async {
      ...
    },
  );
  return stream;
}


void _createErrorHandlerAndKey(
  ImageConfiguration configuration,
  _KeyAndErrorHandlerCallback<T> successCallback,
  _AsyncKeyErrorHandler<T?> errorCallback,
) {
  T? obtainedKey;
  bool didError = false;
  Future<void> handleError(Object exception, StackTrace? stack) async {
    ...
  }

  // 创建一个新Zone,主要是为了当发生错误时不会干扰MainZone
  final Zone dangerZone = Zone.current.fork(...);
  dangerZone.runGuarded(() {
    Future<T> key;
    try {
      // 生成缓存key,后面会根据此key来检测是否有缓存
      key = obtainKey(configuration);
    } catch (error, stackTrace) {
      handleError(error, stackTrace);
      return;
    }
    key.then<void>((T key) {
      obtainedKey = key;
      try {
        // 成功回调
        successCallback(key, hand
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值