欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Flutter加載圖片流程MultiFrameImageStreamCompleter解析

 更新時間:2023年04月20日 10:40:19   作者:Nicholas68  
這篇文章主要為大家介紹了Flutter加載圖片流程MultiFrameImageStreamCompleter示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

MultiFrameImageStreamCompleter

MultiFrameImageStreamCompleter 是一個可組合的 ImageStreamCompleter 類,用于將多個 ImageStreamCompleter 對象合并為一個單獨的 ImageStream 對象,通常用于動畫效果。每當(dāng)子 ImageStreamCompleter 接收到一個新的 ImageInfo 對象,它會立即通知其所有的監(jiān)聽器,并將對象傳遞給它們。

當(dāng) MultiFrameImageStreamCompleteraddListener() 方法被調(diào)用時,它將傳入的 ImageStreamListener 添加到其內(nèi)部的子 ImageStreamCompleter 的監(jiān)聽器列表中。如果 MultiFrameImageStreamCompleter 本身接收到一個 ImageInfo 對象,它會將它傳遞給其所有的監(jiān)聽器。但是,它不會自己管理這些幀,而是委托給每個子 ImageStreamCompleter 來完成。

MultiFrameImageStreamCompleter 還支持漸進(jìn)式 JPEG,并實現(xiàn)了 addListener()removeListener()dispose() 方法,以及一個名為 getNextFrame() 的方法,用于從圖像流中獲取下一幀。

當(dāng)所有幀都加載完畢后,MultiFrameImageStreamCompleter 將使用 dart:ui.Codec 解碼器將它們合并為一個單獨的 dart:ui.Image 對象,并將其傳遞給 setImage() 方法。最后,它將通知所有監(jiān)聽器,并將它們傳遞給 ImageStreamListener.onImage() 回調(diào)函數(shù),以通知它們新的 ImageInfo 已經(jīng)可用。

當(dāng) MultiFrameImageStreamCompleterdispose() 方法被調(diào)用時,它會將其所有子 ImageStreamCompleterdispose() 方法依次調(diào)用,以釋放所有資源,并取消所有未處理的幀請求。同時,它還會確保在釋放資源之前將所有錯誤通知給其監(jiān)聽器。

_handleCodecReady

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

_handleCodecReady方法中,首先將傳入的codec對象賦值給成員變量_codec,然后使用assert語句來確保該變量不為空。接著,如果當(dāng)前對象有監(jiān)聽器,則調(diào)用_decodeNextFrameAndSchedule方法來解碼下一幀并將其調(diào)度執(zhí)行。這里的目的是為了盡早地開始解碼下一幀圖像,以盡快展示出完整的動畫效果。如果沒有監(jiān)聽器,則不需要解碼下一幀圖像,因為沒有地方可以展示它。

_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();
}
  • 這個方法的作用是獲取下一幀并在獲取成功后調(diào)度下一幀的解碼,如果幀數(shù)為1,即這是一個靜態(tài)圖片,則只需返回該幀,并在沒有監(jiān)聽器時直接返回,如果幀數(shù)大于1,則調(diào)度下一幀的解碼。
  • 在獲取下一幀之前,方法會清除上一幀并將_nextFrame置為null,以便準(zhǔn)備下一幀。
  • 如果解碼下一幀時發(fā)生異常,則會記錄錯誤并返回。如果在等待下一幀的解碼期間移除了監(jiān)聽器,則在沒有活動的監(jiān)聽器時不會發(fā)出幀,否則會發(fā)出幀并調(diào)度下一幀的解碼。

_emitFrame 方法的作用是向 ImageStreamCompleter 發(fā)送新的 ImageInfo。具體實現(xiàn)是通過調(diào)用 setImage 方法將 ImageInfo 設(shè)置為 ImageStreamCompleter 的當(dāng)前值,同時更新 _framesEmitted 計數(shù)器。

_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 內(nèi)部解碼圖像幀,返回一個 FrameInfo 對象,其中包含了解碼后的 Image 對象以及該幀的時間戳和持續(xù)時間等信息。由于 Codec 可能會支持動畫圖像,因此 getNextFrame() 方法可能會返回多個幀。

MultiFrameImageStreamCompleter 中,_decodeNextFrameAndSchedule() 方法會調(diào)用 _codec.getNextFrame() 方法獲取下一幀圖像,然后將其保存在 _nextFrame 屬性中。如果 _codecframeCount 屬性為 1,說明這是一個靜態(tài)圖像,直接使用 _emitFrame() 方法發(fā)布該幀圖像;否則,調(diào)用 _scheduleAppFrame() 方法安排下一幀的發(fā)布。

_emitFrame(重要方法, 通知監(jiān)聽器觸發(fā)回調(diào),更新UI)

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

這個方法在 _decodeNextFrameAndSchedule 中被調(diào)用,用于處理已解碼的下一幀圖像。如果當(dāng)前幀是非動畫圖像,它會直接調(diào)用 setImage 方法更新 ImageStreamCompleter 的值,如果是動畫圖像,它會計劃下一幀的顯示并等待下一幀的解碼。

_scheduleAppFrame

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

函數(shù) _scheduleAppFrame() 的作用是調(diào)度一個Flutter引擎幀回調(diào),在回調(diào)中會調(diào)用 _handleAppFrame() 函數(shù)。

具體來說,這個函數(shù)的實現(xiàn)包含以下步驟:

1、檢查 _frameCallbackScheduled 標(biāo)志,如果為 true,則說明幀回調(diào)已經(jīng)被調(diào)度過,直接返回。

2、將 _frameCallbackScheduled 標(biāo)志設(shè)置為 true,表示幀回調(diào)已經(jīng)被調(diào)度。

3、調(diào)用 SchedulerBinding.instance.scheduleFrameCallback() 函數(shù),向Flutter引擎注冊一個幀回調(diào)。回調(diào)函數(shù)為 _handleAppFrame()。

4、在 _handleAppFrame() 函數(shù)中,將會根據(jù)當(dāng)前動畫播放的幀率和幀數(shù),計算下一幀應(yīng)該在何時被顯示,然后再次調(diào)用 _decodeNextFrameAndSchedule() 函數(shù),以獲取并顯示下一幀圖像。這樣就完成了一次動畫播放的循環(huán)。

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

函數(shù) _handleAppFrame 是 MultiFrameImageStreamCompleter 的核心函數(shù),用于處理多幀圖像的邏輯。下面是對該函數(shù)的詳細(xì)解讀:

  • 1、_frameCallbackScheduled = false;

    • _frameCallbackScheduled 設(shè)為 false,表示下一幀還沒有調(diào)度。
  • 2、 if (!hasListeners) { return; }

    • 如果沒有監(jiān)聽器,則直接返回。
  • 3、 assert(_nextFrame != null);

    • 斷言 _nextFrame 不為空。
  • 4、 _isFirstFrame() || _hasFrameDurationPassed(timestamp)

    • 如果是第一幀或者幀時間已經(jīng)超過了 _frameDuration,則進(jìn)行以下操作:
  • 5、 _emitFrame(ImageInfo(image: _nextFrame!.image.clone(), scale: _scale, debugLabel: debugLabel));

    • 發(fā)出 ImageInfo 事件,將 _nextFrame 的圖像信息作為參數(shù)傳入。
  • 6、 _shownTimestamp = timestamp;

    • 更新 _shownTimestamp 為當(dāng)前時間戳。
  • 7、 _frameDuration = _nextFrame!.duration;

    • 更新 _frameDuration_nextFrame 的幀間隔時間。
  • 8、 _nextFrame!.image.dispose(); _nextFrame = null;

    • 釋放 _nextFrame 的圖像資源并將 _nextFrame 設(shè)為 null。
  • 9、 final int completedCycles = _framesEmitted ~/ _codec!.frameCount;

    • 計算已經(jīng)完成的循環(huán)次數(shù)。
  • 10、 _codec!.repetitionCount == -1 || completedCycles <= _codec!.repetitionCount

    • 如果循環(huán)次數(shù)為 -1(表示無限循環(huán))或者已經(jīng)完成的循環(huán)次數(shù)小于等于 _codec 的循環(huán)次數(shù),則進(jìn)行以下操作:
  • 11、 _decodeNextFrameAndSchedule();

    • 解碼下一幀并調(diào)度下一幀的繪制。
  • 12、 final Duration delay = _frameDuration! - (timestamp - _shownTimestamp);

    • 計算下一幀需要延遲的時間。
  • 13、_timer = Timer(delay * timeDilation, () { _scheduleAppFrame(); });

    • 使用計時器來實現(xiàn)下一幀的延遲繪制。延遲時間為 delay 乘以 timeDilation(可以通過調(diào)用 timeDilation = x 來改變時間流逝的速度)。當(dāng)計時器觸發(fā)時,將調(diào)用 _scheduleAppFrame 來調(diào)度下一幀的繪制。

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 添加監(jiān)聽器。當(dāng)?shù)谝粋€監(jiān)聽器被添加到 ImageStreamCompleter 上時,會檢查 _codec 是否為 null,如果不為 null 并且有多幀圖像或者當(dāng)前圖像為 null,則會調(diào)用 _decodeNextFrameAndSchedule() 方法開始解碼下一幀圖像并計劃渲染。這樣做是為了確保在第一個監(jiān)聽器被添加到 ImageStreamCompleter 上時就開始解碼下一幀圖像并在下一幀渲染完成后通知所有監(jiān)聽器。如果 _codec 為 null 或者當(dāng)前圖像為單幀圖像,則不會調(diào)用 _decodeNextFrameAndSchedule() 方法。在這個方法中,調(diào)用了 super.addListener(listener) 將監(jiān)聽器添加到監(jiān)聽器列表中。

removeListener

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

removeListener 方法用于從 MultiFrameImageStreamCompleter 中移除給定的 ImageStreamListener。當(dāng)移除后,如果該對象不再有任何監(jiān)聽器,就會取消定時器 _timer。

具體來說,該方法會先調(diào)用父類的 removeListener 方法,將該監(jiān)聽器從監(jiān)聽器列表中移除。接著,如果此時 hasListenersfalse,說明沒有任何監(jiān)聽器,就會取消 _timer 定時器,以便釋放資源。

_maybeDispose

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

_maybeDispose()是一個用來釋放資源的方法,當(dāng)圖片流不再被監(jiān)聽時調(diào)用。它首先調(diào)用父類的_maybeDispose()方法,以處理父類中的一些釋放資源的邏輯。然后它會檢查_disposed屬性是否為true,如果是,則取消并置空_chunkSubscription,這個對象是用來訂閱圖像數(shù)據(jù)塊的流。這樣做是為了釋放相關(guān)的資源,以防止內(nèi)存泄漏。

總結(jié)

MultiFrameImageStreamCompleter 是 Flutter 中用于處理多幀圖片的類,主要用于將多幀動畫圖片的每一幀渲染到屏幕上。

該類內(nèi)部主要維護(hù)了一個 Codec 對象,用于解碼圖片,同時也有一個 ImageInfo 對象用于存儲當(dāng)前幀的信息,并且該類也實現(xiàn)了 ImageStreamCompleter 類,可以作為 Image 對象的 ImageStream。

在 MultiFrameImageStreamCompleter 的初始化過程中,會創(chuàng)建 Codec 對象,并且在該對象準(zhǔn)備好后進(jìn)行處理,并且在添加監(jiān)聽器時,如果該類當(dāng)前沒有監(jiān)聽器,并且已經(jīng)獲取了第一幀圖像,那么該類會進(jìn)行后續(xù)幀的解碼和渲染。如果該類被銷毀,則會清空 Codec 對象。

在該類的主要方法中,_handleCodecReady() 方法會在初始化時進(jìn)行調(diào)用,用于設(shè)置解碼后的 Codec 對象,并在有監(jiān)聽器的情況下開始解碼和渲染下一幀圖片。

_decodeNextFrameAndSchedule() 方法用于解碼和渲染下一幀圖片,通過 _codec!.getNextFrame() 方法獲取下一幀圖像,并進(jìn)行渲染處理,如果當(dāng)前只有一幀圖片,則直接渲染該幀圖片并停止。

_handleAppFrame() 方法用于處理渲染下一幀圖片的邏輯,會根據(jù)時間戳計算出下一幀圖片的渲染時間,并設(shè)置延時定時器,定時調(diào)用該方法。

addListener() 和 removeListener() 方法用于添加和刪除監(jiān)聽器,并在沒有監(jiān)聽器時停止解碼和渲染。

最后,_maybeDispose() 方法會在該類被銷毀時進(jìn)行調(diào)用,用于清空內(nèi)部緩存。

參考鏈接

深入圖片加載流程

以上就是Flutter加載圖片流程MultiFrameImageStreamCompleter解析的詳細(xì)內(nèi)容,更多關(guān)于Flutter MultiFrameImageStreamCompleter的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論