Flutter MultiFrameImageStreamCompleter是什么

发布时间:2023-04-20 11:32:04 作者:iii
来源:亿速云 阅读:109

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

MultiFrameImageStreamCompleter

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

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

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

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

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

_handleCodecReady

void _handleCodecReady(ui.Codec codec) {
  _codec = codec;
  assert(_codec != null);
  if (hasListeners) {
    _decodeNextFrameAndSchedule();
  }
}

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

_decodeNextFrameAndSchedule

Future<void> _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();
}

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

_codec!.getNextFrame()

_nextFrame = await _codec!.getNextFrame();
/// Fetches the next animation frame.
///
/// Wraps back to the first frame after returning the last frame.
///
/// The returned future can complete with an error if the decoding has failed.
///
/// The caller of this method is responsible for disposing the
/// [FrameInfo.image] on the returned object.
Future<FrameInfo> getNextFrame() async {
  final Completer<FrameInfo> completer = Completer<FrameInfo>.sync();
  final String? error = _getNextFrame((_Image? image, int durationMilliseconds) {
    if (image == null) {
      completer.completeError(Exception('Codec failed to produce an image, possibly due to invalid image data.'));
    } else {
      completer.complete(FrameInfo._(
        image: Image._(image, image.width, image.height),
        duration: Duration(milliseconds: durationMilliseconds),
      ));
    }
  });
  if (error != null) {
    throw Exception(error);
  }
  return completer.future;
}
/// Returns an error message on failure, null on success.
String? _getNextFrame(void Function(_Image?, int) callback) native 'Codec_getNextFrame';

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

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

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

void _emitFrame(ImageInfo imageInfo) {
  setImage(imageInfo);
  _framesEmitted += 1;
}

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

_scheduleAppFrame

void _scheduleAppFrame() {
  if (_frameCallbackScheduled) {
    return;
  }
  _frameCallbackScheduled = true;
  SchedulerBinding.instance.scheduleFrameCallback(_handleAppFrame);
}

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

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

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

2、将 _frameCallbackScheduled 标志设置为 true,表示帧回调已经被调度。

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

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

_handleAppFrame

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;
    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();
  });
}

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

addListener

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

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

removeListener

void removeListener(ImageStreamListener listener) {
  super.removeListener(listener);
  if (!hasListeners) {
    _timer?.cancel();
    _timer = null;
  }
}

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

具体来说,该方法会先调用父类的 removeListener 方法,将该监听器从监听器列表中移除。接着,如果此时 hasListenersfalse,说明没有任何监听器,就会取消 _timer 定时器,以便释放资源。

_maybeDispose

void _maybeDispose() {
  super._maybeDispose();
  if (_disposed) {
    _chunkSubscription?.onData(null);
    _chunkSubscription?.cancel();
    _chunkSubscription = null;
  }
}

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

到此,相信大家对“Flutter MultiFrameImageStreamCompleter是什么”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

推荐阅读:
  1. 如何使用Flutter加载网络图片
  2. 提升Flutter体验的方法是什么

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

flutter

上一篇:Java的JVM类加载机制是什么

下一篇:Python如何实现二分法查找及优化

相关阅读

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

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