本篇内容主要讲解“Flutter加载图片流程之ImageProvider源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Flutter加载图片流程之ImageProvider源码分析”吧!
Image.network()
是Flutter提供的一种从网络上加载图片的方法,它可以从指定的URL加载图片,并在加载完成后将其显示在应用程序中。
ImageProvider
是Flutter中一个抽象类,它定义了一种用于加载图片的通用接口,可以用于加载本地图片、网络图片等各种类型的图片。
ImageProvider
类包含两个核心方法:obtainKey
和loadBuffer
。
/// 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
对象,并将其设置为ImageStream
的completer
属性,表示图片加载失败。
3、如果唯一标识符获取成功,则会调用resolveStreamForKey
方法来解析图片,并将图片数据存储到ImageStream
对象中,供后续使用。
4、该方法是ImageProvider
类层次结构的公共入口点,因为它是所有图片提供器的解析方法。子类只需要实现obtainKey
和load
方法来获取图片的唯一标识符和加载图片的数据,而不需要重写resolve
方法。
5、如果子类需要更改使用的ImageStream
的实现方式,则可以重写createStream
方法。如果子类需要管理实际的图像分辨率,则可以重写resolveStreamForKey
方法。例如,AssetImage
类中的createStream
方法返回一个AssetBundleImageStreamCompleter
对象,该对象用于从应用程序资源中加载图片数据。而NetworkImage
类中的resolveStreamForKey
方法使用HTTP客户端从网络上加载图片数据。
6、这段代码中还有一些调试信息,例如将图片提供器、图片配置和图片唯一标识符添加到调试信息中,以便在出现错误时进行调试。
/// 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<T> obtainKey(ImageConfiguration configuration);
这段注释是关于obtainKey
方法的说明。该方法是ImageProvider
的子类应该实现的方法之一,用于将ImageProvider
的设置及ImageConfiguration
转换为一个可以唯一标识图片的key
。
不同的ImageProvider
根据相同的构造函数参数和ImageConfiguration
对象应该返回相等的key
,以便于后续加载和缓存图片。key
的类型由子类确定,它应该是一个值,可以唯一地标识出要加载的图片(包括其缩放比例)。
在实现obtainKey
方法时,子类可以考虑使用自定义的类来表示key
,并实现==
方法以保证唯一性。
@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); } }
官方文档解释:
该方法是ImageProvider
的子类应该实现的方法之一,用于根据key
来解析图片。
resolveStreamForKey
方法是由resolve
方法调用的,其参数包括ImageConfiguration
、ImageStream
、key
和errorHandler
。子类可以通过实现resolveStreamForKey
方法来管理图片的实际解析过程,同时也可以通过调用errorHandler
来处理解析过程中可能发生的错误。
实现resolveStreamForKey
方法时,子类可以考虑使用key
与ImageCache
交互,例如调用ImageCache.putIfAbsent
方法,并向stream
通知监听器。默认实现已经使用key
与ImageCache
交互,子类可以选择调用super.resolveStreamForKey
方法或不调用。
从上面的源码,我们可以知道以下几点:
1、如果 stream
对象已经有了 completer
(即已经有了可以加载图片的方式),则将 completer
添加到 ImageCache
中,实现缓存功能,并直接返回。
2、如果 stream
对象还没有 completer
,则调用 loadBuffer
方法加载图片,并将其返回的 ImageStreamCompleter
对象添加到 ImageCache
中,同时设置到 stream
对象的 completer
中。
3、如果 loadBuffer
方法出现了异常,则会将异常交给 onError
回调处理,以便在异常处理时能够提供详细的错误信息。
4、关键是解析并设置ImageStreamCompleter
对象
5、PaintingBinding.instance.imageCache.putIfAbsent
方法在内部将ImageStreamListener
对象添加到ImageStreamCompleter
对象的_listeners
数组中了。
PaintingBinding.instance.imageCache.putIfAbsent( key, () => loadBuffer(key, PaintingBinding.instance.instantiateImageCodecFromBuffer), onError: handleError, )
/// 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
方法即可。
这个方法是ImageProvider的一个protected方法,用于从缓存中加载指定的图片。
它接受两个参数:一个是唯一标识图片的key,另一个是一个用于解码图片数据的回调函数decode。
这个方法调用了load方法,然后返回一个ImageStreamCompleter对象,它表示加载过程中的一个数据流。
在load方法中,使用传入的decode回调函数从缓存或网络中获取图片数据并解码,然后将解码后的图片数据传递给ImageStreamCompleter对象,以便它可以生成一个带有正确图片数据的ImageInfo对象,这个ImageInfo对象可以被传递到Image widget中用于显示图片。
/// 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] 回调提供了获取图像编解码器的逻辑。
/// 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<void>((bool success) { /// if (success) { /// debugPrint('removed image!'); /// } /// }); /// } /// } /// ``` /// {@end-tool} Future<bool> evict({ ImageCache? cache, ImageConfiguration configuration = ImageConfiguration.empty }) async { cache ??= imageCache; final T key = await obtainKey(configuration); return cache.evict(key); }
这是一个名为evict
的异步方法,它的作用是从图像缓存中删除给定配置下的图片。它有两个可选参数:cache
和configuration
。如果cache
参数为null,则默认使用全局的imageCache
。configuration
参数是一个图像配置,它用于获取将要从缓存中删除的图片的键值。这个方法返回一个Future<bool>
对象,表示删除是否成功。如果缓存中没有找到要删除的图片,则返回false
。
列表快速滑动,内存暴增时,可以用这个方法做些事情。
第一次加载图片时,stream
对象通常没有completer
。在第一次调用resolveStreamForKey
时,会将stream
对象的completer
与对应的ImageCache
的ImageStreamCompleter
进行绑定,并且completer
会被设置为ImageStreamCompleter
。
到此,相信大家对“Flutter加载图片流程之ImageProvider源码分析”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。