Flutter MultiFrameImageStreamCompleter是什么

其他教程   发布日期:2025年03月21日   浏览次数:123

本篇内容主要讲解“Flutter MultiFrameImageStreamCompleter是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Flutter MultiFrameImageStreamCompleter是什么”吧!

MultiFrameImageStreamCompleter

  1. MultiFrameImageStreamCompleter
是一个可组合的
  1. ImageStreamCompleter
类,用于将多个
  1. ImageStreamCompleter
对象合并为一个单独的
  1. ImageStream
对象,通常用于动画效果。每当子
  1. ImageStreamCompleter
接收到一个新的
  1. ImageInfo
对象,它会立即通知其所有的监听器,并将对象传递给它们。

  1. MultiFrameImageStreamCompleter
  1. addListener()
方法被调用时,它将传入的
  1. ImageStreamListener
添加到其内部的子
  1. ImageStreamCompleter
的监听器列表中。如果
  1. MultiFrameImageStreamCompleter
本身接收到一个
  1. ImageInfo
对象,它会将它传递给其所有的监听器。但是,它不会自己管理这些帧,而是委托给每个子
  1. ImageStreamCompleter
来完成。

  1. MultiFrameImageStreamCompleter
还支持渐进式 JPEG,并实现了
  1. addListener()
  1. removeListener()
  1. dispose()
方法,以及一个名为
  1. getNextFrame()
的方法,用于从图像流中获取下一帧。

当所有帧都加载完毕后,

  1. MultiFrameImageStreamCompleter
将使用
  1. dart:ui.Codec
解码器将它们合并为一个单独的
  1. dart:ui.Image
对象,并将其传递给
  1. setImage()
方法。最后,它将通知所有监听器,并将它们传递给
  1. ImageStreamListener.onImage()
回调函数,以通知它们新的
  1. ImageInfo
已经可用。

  1. MultiFrameImageStreamCompleter
  1. dispose()
方法被调用时,它会将其所有子
  1. ImageStreamCompleter
  1. dispose()
方法依次调用,以释放所有资源,并取消所有未处理的帧请求。同时,它还会确保在释放资源之前将所有错误通知给其监听器。

_handleCodecReady

  1. void _handleCodecReady(ui.Codec codec) {
  2. _codec = codec;
  3. assert(_codec != null);
  4. if (hasListeners) {
  5. _decodeNextFrameAndSchedule();
  6. }
  7. }

  1. _handleCodecReady
方法中,首先将传入的
  1. codec
对象赋值给成员变量
  1. _codec
,然后使用
  1. assert
语句来确保该变量不为空。接着,如果当前对象有监听器,则调用
  1. _decodeNextFrameAndSchedule
方法来解码下一帧并将其调度执行。这里的目的是为了尽早地开始解码下一帧图像,以尽快展示出完整的动画效果。如果没有监听器,则不需要解码下一帧图像,因为没有地方可以展示它。

_decodeNextFrameAndSchedule

  1. Future<void> _decodeNextFrameAndSchedule() async {
  2. // This will be null if we gave it away. If not, it's still ours and it
  3. // must be disposed of.
  4. _nextFrame?.image.dispose();
  5. _nextFrame = null;
  6. try {
  7. _nextFrame = await _codec!.getNextFrame();
  8. } catch (exception, stack) {
  9. reportError(
  10. context: ErrorDescription('resolving an image frame'),
  11. exception: exception,
  12. stack: stack,
  13. informationCollector: _informationCollector,
  14. silent: true,
  15. );
  16. return;
  17. }
  18. if (_codec!.frameCount == 1) {
  19. // ImageStreamCompleter listeners removed while waiting for next frame to
  20. // be decoded.
  21. // There's no reason to emit the frame without active listeners.
  22. if (!hasListeners) {
  23. return;
  24. }
  25. // This is not an animated image, just return it and don't schedule more
  26. // frames.
  27. _emitFrame(ImageInfo(
  28. image: _nextFrame!.image.clone(),
  29. scale: _scale,
  30. debugLabel: debugLabel,
  31. ));
  32. _nextFrame!.image.dispose();
  33. _nextFrame = null;
  34. return;
  35. }
  36. _scheduleAppFrame();
  37. }
  • 这个方法的作用是获取下一帧并在获取成功后调度下一帧的解码,如果帧数为1,即这是一个静态图片,则只需返回该帧,并在没有监听器时直接返回,如果帧数大于1,则调度下一帧的解码。

  • 在获取下一帧之前,方法会清除上一帧并将_nextFrame置为null,以便准备下一帧。

  • 如果解码下一帧时发生异常,则会记录错误并返回。如果在等待下一帧的解码期间移除了监听器,则在没有活动的监听器时不会发出帧,否则会发出帧并调度下一帧的解码。

  1. _emitFrame
方法的作用是向
  1. ImageStreamCompleter
发送新的
  1. ImageInfo
。具体实现是通过调用
  1. setImage
方法将
  1. ImageInfo
设置为
  1. ImageStreamCompleter
的当前值,同时更新
  1. _framesEmitted
计数器。

_codec!.getNextFrame()

  1. _nextFrame = await _codec!.getNextFrame();
  1. /// Fetches the next animation frame.
  2. ///
  3. /// Wraps back to the first frame after returning the last frame.
  4. ///
  5. /// The returned future can complete with an error if the decoding has failed.
  6. ///
  7. /// The caller of this method is responsible for disposing the
  8. /// [FrameInfo.image] on the returned object.
  9. Future<FrameInfo> getNextFrame() async {
  10. final Completer<FrameInfo> completer = Completer<FrameInfo>.sync();
  11. final String? error = _getNextFrame((_Image? image, int durationMilliseconds) {
  12. if (image == null) {
  13. completer.completeError(Exception('Codec failed to produce an image, possibly due to invalid image data.'));
  14. } else {
  15. completer.complete(FrameInfo._(
  16. image: Image._(image, image.width, image.height),
  17. duration: Duration(milliseconds: durationMilliseconds),
  18. ));
  19. }
  20. });
  21. if (error != null) {
  22. throw Exception(error);
  23. }
  24. return completer.future;
  25. }
  26. /// Returns an error message on failure, null on success.
  27. String? _getNextFrame(void Function(_Image?, int) callback) native 'Codec_getNextFrame';

  1. getNextFrame()
  1. Codec
类的一个方法,用于获取解码后的帧。具体来说,它会在
  1. Codec
内部解码图像帧,返回一个
  1. FrameInfo
对象,其中包含了解码后的
  1. Image
对象以及该帧的时间戳和持续时间等信息。由于
  1. Codec
可能会支持动画图像,因此
  1. getNextFrame()
方法可能会返回多个帧。

  1. MultiFrameImageStreamCompleter
中,
  1. _decodeNextFrameAndSchedule()
方法会调用
  1. _codec.getNextFrame()
方法获取下一帧图像,然后将其保存在
  1. _nextFrame
属性中。如果
  1. _codec
  1. frameCount
属性为 1,说明这是一个静态图像,直接使用
  1. _emitFrame()
方法发布该帧图像;否则,调用
  1. _scheduleAppFrame()
方法安排下一帧的发布。

_emitFrame(重要方法, 通知监听器触发回调,更新UI)

  1. void _emitFrame(ImageInfo imageInfo) {
  2. setImage(imageInfo);
  3. _framesEmitted += 1;
  4. }

这个方法在

  1. _decodeNextFrameAndSchedule
中被调用,用于处理已解码的下一帧图像。如果当前帧是非动画图像,它会直接调用
  1. setImage
方法更新
  1. ImageStreamCompleter
的值,如果是动画图像,它会计划下一帧的显示并等待下一帧的解码。

_scheduleAppFrame

  1. void _scheduleAppFrame() {
  2. if (_frameCallbackScheduled) {
  3. return;
  4. }
  5. _frameCallbackScheduled = true;
  6. SchedulerBinding.instance.scheduleFrameCallback(_handleAppFrame);
  7. }

函数

  1. _scheduleAppFrame()
的作用是调度一个Flutter引擎帧回调,在回调中会调用
  1. _handleAppFrame()
函数。

具体来说,这个函数的实现包含以下步骤:

1、检查

  1. _frameCallbackScheduled
标志,如果为 true,则说明帧回调已经被调度过,直接返回。

2、将

  1. _frameCallbackScheduled
标志设置为 true,表示帧回调已经被调度。

3、调用

  1. SchedulerBinding.instance.scheduleFrameCallback()
函数,向Flutter引擎注册一个帧回调。回调函数为
  1. _handleAppFrame()

4、在

  1. _handleAppFrame()
函数中,将会根据当前动画播放的帧率和帧数,计算下一帧应该在何时被显示,然后再次调用
  1. _decodeNextFrameAndSchedule()
函数,以获取并显示下一帧图像。这样就完成了一次动画播放的循环。

_handleAppFrame

  1. void _handleAppFrame(Duration timestamp) {
  2. _frameCallbackScheduled = false;
  3. if (!hasListeners) {
  4. return;
  5. }
  6. assert(_nextFrame != null);
  7. if (_isFirstFrame() || _hasFrameDurationPassed(timestamp)) {
  8. _emitFrame(ImageInfo(
  9. image: _nextFrame!.image.clone(),
  10. scale: _scale,
  11. debugLabel: debugLabel,
  12. ));
  13. _shownTimestamp = timestamp;
  14. _frameDuration = _nextFrame!.duration;
  15. _nextFrame!.image.dispose();
  16. _nextFrame = null;
  17. final int completedCycles = _framesEmitted ~/ _codec!.frameCount;
  18. if (_codec!.repetitionCount == -1 || completedCycles <= _codec!.repetitionCount) {
  19. _decodeNextFrameAndSchedule();
  20. }
  21. return;
  22. }
  23. final Duration delay = _frameDuration! - (timestamp - _shownTimestamp);
  24. _timer = Timer(delay * timeDilation, () {
  25. _scheduleAppFrame();
  26. });
  27. }

函数

  1. _handleAppFrame
是 MultiFrameImageStreamCompleter 的核心函数,用于处理多帧图像的逻辑。下面是对该函数的详细解读:
  • 1、

    1. _frameCallbackScheduled = false;
      1. _frameCallbackScheduled
      设为 false,表示下一帧还没有调度。
  • 2、

    1. if (!hasListeners) { return; }
    • 如果没有监听器,则直接返回。

  • 3、

    1. assert(_nextFrame != null);
    • 断言

      1. _nextFrame
      不为空。
  • 4、

    1. _isFirstFrame() || _hasFrameDurationPassed(timestamp)
    • 如果是第一帧或者帧时间已经超过了

      1. _frameDuration
      ,则进行以下操作:
  • 5、

    1. _emitFrame(ImageInfo(image: _nextFrame!.image.clone(), scale: _scale, debugLabel: debugLabel));
    • 发出

      1. ImageInfo
      事件,将
      1. _nextFrame
      的图像信息作为参数传入。
  • 6、

    1. _shownTimestamp = timestamp;
    • 更新

      1. _shownTimestamp
      为当前时间戳。
  • 7、

    1. _frameDuration = _nextFrame!.duration;
    • 更新

      1. _frameDuration
      1. _nextFrame
      的帧间隔时间。
  • 8、

    1. _nextFrame!.image.dispose(); _nextFrame = null;
    • 释放

      1. _nextFrame
      的图像资源并将
      1. _nextFrame
      设为 null。
  • 9、

    1. final int completedCycles = _framesEmitted ~/ _codec!.frameCount;
    • 计算已经完成的循环次数。

  • 10、

    1. _codec!.repetitionCount == -1 || completedCycles <= _codec!.repetitionCount
    • 如果循环次数为 -1(表示无限循环)或者已经完成的循环次数小于等于

      1. _codec
      的循环次数,则进行以下操作:
  • 11、

    1. _decodeNextFrameAndSchedule();
    • 解码下一帧并调度下一帧的绘制。

  • 12、

    1. final Duration delay = _frameDuration! - (timestamp - _shownTimestamp);
    • 计算下一帧需要延迟的时间。

  • 13、

    1. _timer = Timer(delay * timeDilation, () { _scheduleAppFrame(); });
    • 使用计时器来实现下一帧的延迟绘制。延迟时间为

      1. delay
      乘以
      1. timeDilation
      (可以通过调用
      1. timeDilation = x
      来改变时间流逝的速度)。当计时器触发时,将调用
      1. _scheduleAppFrame
      来调度下一帧的绘制。

addListener

  1. void addListener(ImageStreamListener listener) {
  2. if (!hasListeners &amp;&amp; _codec != null &amp;&amp; (_currentImage == null || _codec!.frameCount &gt; 1)) {
  3. _decodeNextFrameAndSchedule();
  4. }
  5. super.addListener(listener);
  6. }

这个方法是

  1. ImageStreamCompleter
类的方法,用于向
  1. ImageStreamCompleter
添加监听器。当第一个监听器被添加到
  1. ImageStreamCompleter
上时,会检查
  1. _codec
是否为 null,如果不为 null 并且有多帧图像或者当前图像为 null,则会调用
  1. _decodeNextFrameAndSchedule()
方法开始解码下一帧图像并计划渲染。这样做是为了确保在第一个监听器被添加到
  1. ImageStreamCompleter
上时就开始解码下一帧图像并在下一帧渲染完成后通知所有监听器。如果
  1. _codec
为 null 或者当前图像为单帧图像,则不会调用
  1. _decodeNextFrameAndSchedule()
方法。在这个方法中,调用了
  1. super.addListener(listener)
将监听器添加到监听器列表中。

removeListener

  1. void removeListener(ImageStreamListener listener) {
  2. super.removeListener(listener);
  3. if (!hasListeners) {
  4. _timer?.cancel();
  5. _timer = null;
  6. }
  7. }

  1. removeListener
方法用于从
  1. MultiFrameImageStreamCompleter
中移除给定的
  1. ImageStreamListener
。当移除后,如果该对象不再有任何监听器,就会取消定时器
  1. _timer

具体来说,该方法会先调用父类的

  1. removeListener
方法,将该监听器从监听器列表中移除。接着,如果此时
  1. hasListeners
  1. false
,说明没有任何监听器,就会取消
  1. _timer
定时器,以便释放资源。

_maybeDispose

  1. void _maybeDispose() {
  2. super._maybeDispose();
  3. if (_disposed) {
  4. _chunkSubscription?.onData(null);
  5. _chunkSubscription?.cancel();
  6. _chunkSubscription = null;
  7. }
  8. }

  1. _maybeDispose()
是一个用来释放资源的方法,当图片流不再被监听时调用。它首先调用父类的
  1. _maybeDispose()
方法,以处理父类中的一些释放资源的逻辑。然后它会检查
  1. _disposed
属性是否为true,如果是,则取消并置空
  1. _chunkSubscription
,这个对象是用来订阅图像数据块的流。这样做是为了释放相关的资源,以防止内存泄漏。

以上就是Flutter MultiFrameImageStreamCompleter是什么的详细内容,更多关于Flutter MultiFrameImageStreamCompleter是什么的资料请关注九品源码其它相关文章!