Flutter加载图片流程之ImageProvider源码分析

发布时间:2023-04-20 15:59:58 作者:iii
来源:亿速云 阅读:112

本篇内容主要讲解“Flutter加载图片流程之ImageProvider源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Flutter加载图片流程之ImageProvider源码分析”吧!

加载网络图片

Image.network()是Flutter提供的一种从网络上加载图片的方法,它可以从指定的URL加载图片,并在加载完成后将其显示在应用程序中。

ImageProvider

ImageProvider是Flutter中一个抽象类,它定义了一种用于加载图片的通用接口,可以用于加载本地图片、网络图片等各种类型的图片。

ImageProvider类包含两个核心方法:obtainKeyloadBuffer

resolve

/// 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. If they need to change the implementation of [ImageStream] used,
/// they should override [createStream]. If they need to manage the actual
/// resolution of the image, they should override [resolveStreamForKey].
///
/// See the Lifecycle documentation on [ImageProvider] for more information.
@nonVirtual
ImageStream resolve(ImageConfiguration configuration) {
  assert(configuration != null);
  final ImageStream stream = createStream(configuration);
  // Load the key (potentially asynchronously), set up an error handling zone,
  // and call resolveStreamForKey.
  _createErrorHandlerAndKey(
    configuration,
    (T key, ImageErrorListener errorHandler) {
      resolveStreamForKey(configuration, stream, key, errorHandler);
    },
    (T? key, Object exception, StackTrace? stack) async {
      await null; // wait an event turn in case a listener has been added to the image stream.
      InformationCollector? collector;
      assert(() {
        collector = () => <DiagnosticsNode>[          DiagnosticsProperty<ImageProvider>('Image provider', this),          DiagnosticsProperty<ImageConfiguration>('Image configuration', configuration),          DiagnosticsProperty<T>('Image key', key, defaultValue: null),        ];
        return true;
      }());
      if (stream.completer == null) {
        stream.setCompleter(_ErrorImageCompleter());
      }
      stream.completer!.reportError(
        exception: exception,
        stack: stack,
        context: ErrorDescription('while resolving an image'),
        silent: true, // could be a network error or whatnot
        informationCollector: collector,
      );
    },
  );
  return stream;
}

根据文档解释,我们可以了解到以下几点:

1、使用给定的`configuration`解析该图片提供器,返回一个 [ImageStream]。  

2、这是 [ImageProvider] 类层次结构的公共入口点。

3、子类应该实现 [obtainKey] 和 [load] 方法,这两个方法将被该方法使用。 

4、如果子类需要更改使用的 [ImageStream] 的实现,则应该重写 [createStream] 方法。 

5、 如果子类需要管理实际的图像分辨率,则应该重写 [resolveStreamForKey] 方法。 

阅读resolve方法的实现。我们可以知道:

1、它使用给定的configuration参数创建一个ImageStream对象(createStream)。然后调用_createErrorHandlerAndKey方法,该方法会异步获取图片的唯一标识符,并设置一个错误处理区域,以防图片加载过程中发生错误。

2、如果获取唯一标识符的过程中出现异常,则会将错误信息封装成一个_ErrorImageCompleter对象,并将其设置为ImageStreamcompleter属性,表示图片加载失败。

3、如果唯一标识符获取成功,则会调用resolveStreamForKey方法来解析图片,并将图片数据存储到ImageStream对象中,供后续使用。

4、该方法是ImageProvider类层次结构的公共入口点,因为它是所有图片提供器的解析方法。子类只需要实现obtainKeyload方法来获取图片的唯一标识符和加载图片的数据,而不需要重写resolve方法。

5、如果子类需要更改使用的ImageStream的实现方式,则可以重写createStream方法。如果子类需要管理实际的图像分辨率,则可以重写resolveStreamForKey方法。例如,AssetImage类中的createStream方法返回一个AssetBundleImageStreamCompleter对象,该对象用于从应用程序资源中加载图片数据。而NetworkImage类中的resolveStreamForKey方法使用HTTP客户端从网络上加载图片数据。

6、这段代码中还有一些调试信息,例如将图片提供器、图片配置和图片唯一标识符添加到调试信息中,以便在出现错误时进行调试。

obtainKey

/// 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 [==]).
Future&lt;T&gt; obtainKey(ImageConfiguration configuration);

这段注释是关于obtainKey方法的说明。该方法是ImageProvider的子类应该实现的方法之一,用于将ImageProvider的设置及ImageConfiguration转换为一个可以唯一标识图片的key

不同的ImageProvider根据相同的构造函数参数和ImageConfiguration对象应该返回相等的key,以便于后续加载和缓存图片。key的类型由子类确定,它应该是一个值,可以唯一地标识出要加载的图片(包括其缩放比例)。

在实现obtainKey方法时,子类可以考虑使用自定义的类来表示key,并实现==方法以保证唯一性。

resolveStreamForKey

@protected
void resolveStreamForKey(ImageConfiguration configuration, ImageStream stream, T key, ImageErrorListener handleError) {
  // This is an unusual edge case where someone has told us that they found
  // the image we want before getting to this method. We should avoid calling
  // load again, but still update the image cache with LRU information.
  if (stream.completer != null) {
    final ImageStreamCompleter? completer = PaintingBinding.instance.imageCache.putIfAbsent(
      key,
      () => stream.completer!,
      onError: handleError,
    );
    assert(identical(completer, stream.completer));
    return;
  }
  final ImageStreamCompleter? completer = PaintingBinding.instance.imageCache.putIfAbsent(
    key,
    /// 加载
    () => loadBuffer(key, PaintingBinding.instance.instantiateImageCodecFromBuffer),
    onError: handleError,
  );
  if (completer != null) {
    /// 关键是解析并设置ImageStreamCompleter对象
    stream.setCompleter(completer);
  }
}

官方文档解释:

从上面的源码,我们可以知道以下几点:

   PaintingBinding.instance.imageCache.putIfAbsent(
     key,
     () =&gt; loadBuffer(key, PaintingBinding.instance.instantiateImageCodecFromBuffer),
     onError: handleError,
   )

loadBuffer

/// Converts a key into an [ImageStreamCompleter], and begins fetching the
/// image.
///
/// For backwards-compatibility the default implementation of this method calls
/// through to [ImageProvider.load]. However, implementors of this interface should
/// only override this method and not [ImageProvider.load], which is deprecated.
///
/// The [decode] callback provides the logic to obtain the codec for the
/// image.
///
/// See also:
///
///  * [ResizeImage], for modifying the key to account for cache dimensions.
@protected
ImageStreamCompleter loadBuffer(T key, DecoderBufferCallback decode) {
  return load(key, PaintingBinding.instance.instantiateImageCodec);
}

从源码我们知道, [ImageProvider.load], which is deprecated被废弃了。子类只需要重写loadBuffer方法即可。

load(被废弃)

/// Converts a key into an [ImageStreamCompleter], and begins fetching the
/// image.
///
/// This method is deprecated. Implement [loadBuffer] for faster image
/// loading. Only one of [load] and [loadBuffer] must be implemented, and
/// [loadBuffer] is preferred.
///
/// The [decode] callback provides the logic to obtain the codec for the
/// image.
///
/// See also:
///
///  * [ResizeImage], for modifying the key to account for cache dimensions.
@protected
@Deprecated(
  'Implement loadBuffer for faster image loading. '
  'This feature was deprecated after v2.13.0-1.0.pre.',
)
ImageStreamCompleter load(T key, DecoderCallback decode) {
  throw UnsupportedError('Implement loadBuffer for faster image loading');
}

从注释可知:

这个方法被废弃了,现在已经不再建议使用了。如果需要更快的图像加载,请实现 [loadBuffer] 方法。在 [load] 和 [loadBuffer] 方法中只需要实现其中一个,而且 [loadBuffer] 更受推荐。

[decode] 回调提供了获取图像编解码器的逻辑。

evict

/// Evicts an entry from the image cache.
///
/// Returns a [Future] which indicates whether the value was successfully
/// removed.
///
/// The [ImageProvider] used does not need to be the same instance that was
/// passed to an [Image] widget, but it does need to create a key which is
/// equal to one.
///
/// The [cache] is optional and defaults to the global image cache.
///
/// The [configuration] is optional and defaults to
/// [ImageConfiguration.empty].
///
/// {@tool snippet}
///
/// The following sample code shows how an image loaded using the [Image]
/// widget can be evicted using a [NetworkImage] with a matching URL.
///
/// ```dart
/// class MyWidget extends StatelessWidget {
///   const MyWidget({
///     super.key,
///     this.url = ' ... ',
///   });
///
///   final String url;
///
///   @override
///   Widget build(BuildContext context) {
///     return Image.network(url);
///   }
///
///   void evictImage() {
///     final NetworkImage provider = NetworkImage(url);
///     provider.evict().then&lt;void&gt;((bool success) {
///       if (success) {
///         debugPrint('removed image!');
///       }
///     });
///   }
/// }
/// ```
/// {@end-tool}
Future&lt;bool&gt; evict({ ImageCache? cache, ImageConfiguration configuration = ImageConfiguration.empty }) async {
  cache ??= imageCache;
  final T key = await obtainKey(configuration);
  return cache.evict(key);
}

这是一个名为evict的异步方法,它的作用是从图像缓存中删除给定配置下的图片。它有两个可选参数:cacheconfiguration。如果cache参数为null,则默认使用全局的imageCacheconfiguration参数是一个图像配置,它用于获取将要从缓存中删除的图片的键值。这个方法返回一个Future<bool>对象,表示删除是否成功。如果缓存中没有找到要删除的图片,则返回false

列表快速滑动,内存暴增时,可以用这个方法做些事情。

困惑解答

第一次加载图片时,stream对象通常没有completer。在第一次调用resolveStreamForKey时,会将stream对象的completer与对应的ImageCacheImageStreamCompleter进行绑定,并且completer会被设置为ImageStreamCompleter

到此,相信大家对“Flutter加载图片流程之ImageProvider源码分析”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

推荐阅读:
  1. Android怎么在原生App中嵌入Flutter
  2. 使用Flutter怎么对JSON进行解析

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

flutter

上一篇:Vue中的nextTick方法怎么使用

下一篇:synchronized、volatile、ReentrantLock的区别是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》