From 7fbf8efc8db3a6c56088352a8ade264070a59154 Mon Sep 17 00:00:00 2001 From: luwnejie Date: Thu, 30 May 2024 16:02:27 +0800 Subject: [PATCH] replace Stream with Future --- .../lib/cached_network_image.dart | 1 - .../lib/src/image_provider/_image_loader.dart | 1 - .../cached_network_image_provider.dart | 9 +- .../multi_image_stream_completer.dart | 264 ------------------ 4 files changed, 4 insertions(+), 271 deletions(-) delete mode 100644 cached_network_image/lib/src/image_provider/multi_image_stream_completer.dart diff --git a/cached_network_image/lib/cached_network_image.dart b/cached_network_image/lib/cached_network_image.dart index 99375aaa..317d8c87 100644 --- a/cached_network_image/lib/cached_network_image.dart +++ b/cached_network_image/lib/cached_network_image.dart @@ -7,4 +7,3 @@ export 'package:flutter_cache_manager/flutter_cache_manager.dart' export 'src/cached_image_widget.dart'; export 'src/image_provider/cached_network_image_provider.dart'; -export 'src/image_provider/multi_image_stream_completer.dart'; diff --git a/cached_network_image/lib/src/image_provider/_image_loader.dart b/cached_network_image/lib/src/image_provider/_image_loader.dart index a7304e07..54d714ce 100644 --- a/cached_network_image/lib/src/image_provider/_image_loader.dart +++ b/cached_network_image/lib/src/image_provider/_image_loader.dart @@ -108,7 +108,6 @@ class ImageLoader implements platform.ImageLoader { headers: headers, key: cacheKey, ); - await for (final result in stream) { if (result is DownloadProgress) { chunkEvents.add( diff --git a/cached_network_image/lib/src/image_provider/cached_network_image_provider.dart b/cached_network_image/lib/src/image_provider/cached_network_image_provider.dart index d853c535..08111b1c 100644 --- a/cached_network_image/lib/src/image_provider/cached_network_image_provider.dart +++ b/cached_network_image/lib/src/image_provider/cached_network_image_provider.dart @@ -1,7 +1,6 @@ import 'dart:async' show Future, StreamController; import 'dart:ui' as ui show Codec; -import 'package:cached_network_image/src/image_provider/multi_image_stream_completer.dart'; import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart' show ErrorListener, ImageRenderMethodForWeb; import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart' @@ -74,8 +73,8 @@ class CachedNetworkImageProvider DecoderBufferCallback decode, ) { final chunkEvents = StreamController(); - final imageStreamCompleter = MultiImageStreamCompleter( - codec: _loadBufferAsync(key, chunkEvents, decode), + final imageStreamCompleter = MultiFrameImageStreamCompleter( + codec: _loadBufferAsync(key, chunkEvents, decode).first, chunkEvents: chunkEvents.stream, scale: key.scale, informationCollector: () sync* { @@ -128,8 +127,8 @@ class CachedNetworkImageProvider ImageDecoderCallback decode, ) { final chunkEvents = StreamController(); - final imageStreamCompleter = MultiImageStreamCompleter( - codec: _loadImageAsync(key, chunkEvents, decode), + final imageStreamCompleter = MultiFrameImageStreamCompleter( + codec: _loadImageAsync(key, chunkEvents, decode).first, chunkEvents: chunkEvents.stream, scale: key.scale, informationCollector: () sync* { diff --git a/cached_network_image/lib/src/image_provider/multi_image_stream_completer.dart b/cached_network_image/lib/src/image_provider/multi_image_stream_completer.dart deleted file mode 100644 index 76f6a806..00000000 --- a/cached_network_image/lib/src/image_provider/multi_image_stream_completer.dart +++ /dev/null @@ -1,264 +0,0 @@ -import 'dart:async'; -import 'dart:ui' as ui show Codec, FrameInfo; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/painting.dart'; -import 'package:flutter/scheduler.dart'; - -/// Slows down animations by this factor to help in development. -double get timeDilation => _timeDilation; -double _timeDilation = 1; - -/// An ImageStreamCompleter with support for loading multiple images. -class MultiImageStreamCompleter extends ImageStreamCompleter { - /// The constructor to create an MultiImageStreamCompleter. The [codec] - /// should be a stream with the images that should be shown. The - /// [chunkEvents] should indicate the [ImageChunkEvent]s of the first image - /// to show. - MultiImageStreamCompleter({ - required Stream codec, - required double scale, - String? debugLabel, - Stream? chunkEvents, - InformationCollector? informationCollector, - }) : _informationCollector = informationCollector, - _scale = scale { - codec.listen( - (event) { - if (_timer != null) { - _nextImageCodec = event; - } else { - _handleCodecReady(event); - } - }, - onError: (Object error, StackTrace stack) { - reportError( - context: ErrorDescription('resolving an image codec'), - exception: error, - stack: stack, - informationCollector: informationCollector, - silent: true, - ); - }, - ); - if (chunkEvents != null) { - _chunkSubscription = chunkEvents.listen( - reportImageChunkEvent, - onError: (Object error, StackTrace stack) { - reportError( - context: ErrorDescription('loading an image'), - exception: error, - stack: stack, - informationCollector: informationCollector, - silent: true, - ); - }, - ); - } - } - - ui.Codec? _codec; - ui.Codec? _nextImageCodec; - final double _scale; - final InformationCollector? _informationCollector; - ui.FrameInfo? _nextFrame; - - // When the current was first shown. - late Duration _shownTimestamp; - - // The requested duration for the current frame; - Duration? _frameDuration; - - // How many frames have been emitted so far. - int _framesEmitted = 0; - Timer? _timer; - StreamSubscription? _chunkSubscription; - - // Used to guard against registering multiple _handleAppFrame callbacks for the same frame. - bool _frameCallbackScheduled = false; - - /// We must avoid disposing a completer if it never had a listener, even - /// if all [keepAlive] handles get disposed. - bool __hadAtLeastOneListener = false; - - bool __disposed = false; - - void _switchToNewCodec() { - _framesEmitted = 0; - _timer = null; - _handleCodecReady(_nextImageCodec!); - _nextImageCodec = null; - } - - void _handleCodecReady(ui.Codec codec) { - _codec = codec; - - if (hasListeners) { - _decodeNextFrameAndSchedule(); - } - } - - void _handleAppFrame(Duration timestamp) { - _frameCallbackScheduled = false; - if (!hasListeners) { - return; - } - assert(_nextFrame != null); - if (_isFirstFrame() || _hasFrameDurationPassed(timestamp)) { - _emitFrame(ImageInfo( - image: _nextFrame!.image.clone(), - scale: _scale, - debugLabel: debugLabel, - )); - _shownTimestamp = timestamp; - _frameDuration = _nextFrame!.duration; - _nextFrame!.image.dispose(); - _nextFrame = null; - - if (_framesEmitted % _codec!.frameCount == 0 && _nextImageCodec != null) { - _switchToNewCodec(); - } else { - final int completedCycles = _framesEmitted ~/ _codec!.frameCount; - if (_codec!.repetitionCount == -1 || - completedCycles <= _codec!.repetitionCount) { - _decodeNextFrameAndSchedule(); - } - } - return; - } - final Duration delay = _frameDuration! - (timestamp - _shownTimestamp); - _timer = Timer(delay * timeDilation, () { - _scheduleAppFrame(); - }); - } - - bool _isFirstFrame() { - return _frameDuration == null; - } - - bool _hasFrameDurationPassed(Duration timestamp) { - return timestamp - _shownTimestamp! >= _frameDuration!; - } - - Future _decodeNextFrameAndSchedule() async { - // This will be null if we gave it away. If not, it's still ours and it - // must be disposed of. - _nextFrame?.image.dispose(); - _nextFrame = null; - try { - _nextFrame = await _codec!.getNextFrame(); - } catch (exception, stack) { - reportError( - context: ErrorDescription('resolving an image frame'), - exception: exception, - stack: stack, - informationCollector: _informationCollector, - silent: true, - ); - return; - } - if (_codec!.frameCount == 1) { - // ImageStreamCompleter listeners removed while waiting for next frame to - // be decoded. - // There's no reason to emit the frame without active listeners. - if (!hasListeners) { - return; - } - // This is not an animated image, just return it and don't schedule more - // frames. - _emitFrame(ImageInfo( - image: _nextFrame!.image.clone(), - scale: _scale, - debugLabel: debugLabel, - )); - _nextFrame!.image.dispose(); - _nextFrame = null; - return; - } - _scheduleAppFrame(); - } - - void _scheduleAppFrame() { - if (_frameCallbackScheduled) { - return; - } - _frameCallbackScheduled = true; - SchedulerBinding.instance.scheduleFrameCallback(_handleAppFrame); - } - - void _emitFrame(ImageInfo imageInfo) { - setImage(imageInfo); - _framesEmitted += 1; - } - - @override - void addListener(ImageStreamListener listener) { - __hadAtLeastOneListener = true; - if (!hasListeners && - _codec != null && - ( //_currentImage == null || - _codec!.frameCount > 1)) { - _decodeNextFrameAndSchedule(); - } - super.addListener(listener); - } - - @override - void removeListener(ImageStreamListener listener) { - super.removeListener(listener); - if (!hasListeners) { - _timer?.cancel(); - _timer = null; - __maybeDispose(); - } - } - - int __keepAliveHandles = 0; - - @override - ImageStreamCompleterHandle keepAlive() { - final delegateHandle = super.keepAlive(); - return _MultiImageStreamCompleterHandle(this, delegateHandle); - } - - void __maybeDispose() { - if (!__hadAtLeastOneListener || - __disposed || - hasListeners || - __keepAliveHandles != 0) { - return; - } - - __disposed = true; - - _chunkSubscription?.onData(null); - _chunkSubscription?.cancel(); - _chunkSubscription = null; - } -} - -class _MultiImageStreamCompleterHandle implements ImageStreamCompleterHandle { - _MultiImageStreamCompleterHandle(this._completer, this._delegateHandle) { - _completer!.__keepAliveHandles += 1; - } - - MultiImageStreamCompleter? _completer; - final ImageStreamCompleterHandle _delegateHandle; - - /// Call this method to signal the [ImageStreamCompleter] that it can now be - /// disposed when its last listener drops. - /// - /// This method must only be called once per object. - @override - void dispose() { - assert(_completer != null); - assert(_completer!.__keepAliveHandles > 0); - assert(!_completer!.__disposed); - - _delegateHandle.dispose(); - - _completer!.__keepAliveHandles -= 1; - _completer!.__maybeDispose(); - _completer = null; - } -}