日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

探索Flutter_Image显示Webp逻辑

發布時間:2025/3/19 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 探索Flutter_Image显示Webp逻辑 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文主要介紹探索Flutter_Image顯示Webp邏輯

簡介

最近探索了一下新增Flutter的Image widget對webp做一個stopAnimation的拓展的Api,順便了解一下Image整個結構和對一些多幀圖片的處理。 我們先看看Image的一個類圖結構。

其中:

  • ImageProvider 提供加載圖片的入口,不同的圖片資源加載方式不一樣,只要重寫其load方法即可。同樣,緩存圖片的key值也有其生成。
  • FileImage 負責讀取文件圖片的數據,讀取到的文件數據轉化成ui.Codec對象交給ImageStreamCompleter去處理解析。
  • ImageStreamCompleter就是逐幀解析圖片的類,生成之后會加入ImageCache,下載可以從緩存中得到。
  • ImageStream是處理Image Resource的,ImageState通過ImageStream與ImageStreamCompleter建立聯系。ImageStream里也存儲著圖片加載完畢的監聽回調。
  • MultiFrameImageStreamCompleter就是多幀圖片解析器。 Flutter imgae支持的圖片格式為:JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, and WBMP。Flutter Image是顯示圖片的一個Widget。 Flutter Image的幾個構造方法:
方法釋義
Image()從ImageProvider中獲取圖片,從本質上看,下面的幾個方法都是他的具體實現。
Image.asset(String name)從AssetBundler中獲取圖片
Image.network(String src)顯示網絡圖片
Image.file(File file)從文件中獲取圖片
Image.memory(Uint8List bytes)從Uint8List獲取數據顯示圖片

Image

從Image的構造體上看,ImageProvider才是圖片提供方,所以我們后面會看看ImageProvider究竟是要做點什么的。 其他的參數是一些圖片的屬性和一些builder。

ImageState

關鍵代碼:

void didUpdateWidget(Image oldWidget) {super.didUpdateWidget(oldWidget);if (_isListeningToStream &&(widget.loadingBuilder == null) != (oldWidget.loadingBuilder == null)) {_imageStream.removeListener(_getListener(oldWidget.loadingBuilder));_imageStream.addListener(_getListener());}if (widget.image != oldWidget.image)_resolveImage();}

ImageProvider

其實ImageProvider是一個抽象類,讓需要定制的子類去做一些實現。

比如:FileImage、MemoryImage、ExactAssetImage等等。其中對FileImage的代碼進行了一些分析。

class FileImage extends ImageProvider<FileImage> {/// Creates an object that decodes a [File] as an image.////// The arguments must not be null.const FileImage(this.file, { this.scale: 1.0 }): assert(file != null),assert(scale != null);/// The file to decode into an image.final File file;/// The scale to place in the [ImageInfo] object of the image.final double scale;@overrideFuture<FileImage> obtainKey(ImageConfiguration configuration) {return new SynchronousFuture<FileImage>(this);}@overrideImageStreamCompleter load(FileImage key) {return new MultiFrameImageStreamCompleter(codec: _loadAsync(key),scale: key.scale,informationCollector: (StringBuffer information) {information.writeln('Path: ${file?.path}');});}Future<ui.Codec> _loadAsync(FileImage key) async {assert(key == this);final Uint8List bytes = await file.readAsBytes();if (bytes.lengthInBytes == 0)return null;return await ui.instantiateImageCodec(bytes);}@overridebool operator ==(dynamic other) {if (other.runtimeType != runtimeType)return false;final FileImage typedOther = other;return file?.path == typedOther.file?.path&& scale == typedOther.scale;}@overrideint get hashCode => hashValues(file?.path, scale);@overrideString toString() => '$runtimeType("${file?.path}", scale: $scale)'; }

FileImage重寫了 obtainKey、load的方法。但是在什么地方會調用這兩個重寫的方法呢?那肯定是ImageProvider這個父類了。

@optionalTypeArgs abstract class ImageProvider<T> {/// Abstract const constructor. This constructor enables subclasses to provide/// const constructors so that they can be used in const expressions.const ImageProvider();/// 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.ImageStream resolve(ImageConfiguration configuration) {assert(configuration != null);final ImageStream stream = new ImageStream();T obtainedKey;obtainKey(configuration).then<void>((T key) {obtainedKey = key;stream.setCompleter(PaintingBinding.instance.imageCache.putIfAbsent(key, () => load(key)));}).catchError((dynamic exception, StackTrace stack) async {FlutterError.reportError(new FlutterErrorDetails(exception: exception,stack: stack,library: 'services library',context: 'while resolving an image',silent: true, // could be a network error or whatnotinformationCollector: (StringBuffer information) {information.writeln('Image provider: $this');information.writeln('Image configuration: $configuration');if (obtainedKey != null)information.writeln('Image key: $obtainedKey');}));return null;});return stream;}/// 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 [==]).@protectedFuture<T> obtainKey(ImageConfiguration configuration);/// Converts a key into an [ImageStreamCompleter], and begins fetching the/// image.@protectedImageStreamCompleter load(T key);@overrideString toString() => '$runtimeType()'; }

該方法的作用就是創建一個ImageStream,并且ImageConfiguration作為key從ImageCache中獲取ImageCompleter,設置到ImageStream上面。而ImageCompleter是為了設置一些回調和幫助ImageStream設置圖片的一個類。

ImageConfiguration是對于ImageCompleter的一些配置。

ImageCache是對于ImageCompleter的緩存。 ImageStreamCompleter putIfAbsent(Object key, ImageStreamCompleter loader(), { ImageErrorListener onError }) 這個方法在resolve方法中是一個關鍵方法。

ImageStreamCompleter putIfAbsentImageStreamCompleter putIfAbsent(Object key, ImageStreamCompleter loader()) {assert(key != null);assert(loader != null);ImageStreamCompleter result = _cache[key];if (result != null) {// Remove the provider from the list so that we can put it back in below// and thus move it to the end of the list._cache.remove(key);} else {if (_cache.length == maximumSize && maximumSize > 0)_cache.remove(_cache.keys.first);result = loader();}if (maximumSize > 0) {assert(_cache.length < maximumSize);_cache[key] = result;}assert(_cache.length <= maximumSize);return result;}

這個方法是在imageCache里面的,提供的是內存緩存api的入口方法,putIfAbsent會先通過key獲取之前的ImageStreamCompleter對象,這個key就是NetworkImage對象,當然我們也可以重寫obtainKey方法自定義key,如果存在則直接返回,如果不存在則執行load方法加載ImageStreamCompleter對象,并將其放到首位(最少最近使用算法)。 也就是說ImageProvider已經實現了內存緩存:默認緩存圖片的最大個數是1000,默認緩存圖片的最大空間是10MiB。 第一次加載圖片肯定是沒有緩存的,所以會調用loader方法,那就是方法外面傳進去的load()方法。

FileImage的load方法

@override ImageStreamCompleter load(FileImage key) {return new MultiFrameImageStreamCompleter(codec: _loadAsync(key),scale: key.scale,informationCollector: (StringBuffer information) {information.writeln('Path: ${file?.path}');}); }Future<ui.Codec> _loadAsync(FileImage key) async {assert(key == this);final Uint8List bytes = await file.readAsBytes();if (bytes.lengthInBytes == 0)return null;return await ui.instantiateImageCodec(bytes); }

load方法中使用了一個叫MultiFrameImageStreamCompleter的類:

MultiFrameImageStreamCompleter({@required Future<ui.Codec> codec,@required double scale,InformationCollector informationCollector }) : assert(codec != null),_informationCollector = informationCollector,_scale = scale,_framesEmitted = 0,_timer = null {codec.then<void>(_handleCodecReady, onError: (dynamic error, StackTrace stack) {FlutterError.reportError(new FlutterErrorDetails(exception: error,stack: stack,library: 'services',context: 'resolving an image codec',informationCollector: informationCollector,silent: true,));}); }

MultiFrameImageStreamCompleter是ImageStreamCompleter的子類,為了處理多幀的圖片加載,Flutter的Image支持加載webp,通過MultiFrameImageStreamCompleter可以對webp文件進行解析,MultiFrameImageStreamCompleter拿到外面傳入的codec數據對象,通過handleCodecReady來保存Codec,之后調用decodeNextFrameAndSchedule方法,從Codec獲取下一幀圖片數據和把數據通知回調到Image,并且開啟定時解析下一幀圖片數據。

到此為止,基本dart流程就走完了,所以需要做stopAnimation和startAnimation的改造就應該這這個MultiFrameImageStreamCompleter入手了。

最后

整個在Dart層面Image解析webp的流程就這樣,

總結

以上是生活随笔為你收集整理的探索Flutter_Image显示Webp逻辑的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。