Flutter加载图片流程之ImageStream源码解析(五)

568 阅读4分钟

ImageStream

ImageStream是一个指向图像资源的句柄,由一个[ImageInfo]对象表示,其中包含[dart:ui.Image]对象和其比例信息。 ImageStream对象可以代表一个未完成加载的图像。它们被[ImageStreamCompleter]对象支持。

当添加至少一个监听器并且监听器计数减少到零时,[ImageCache]会将图像视为实时的。[ImageStreamCompleter.addOnLastListenerRemovedCallback]方法用于跟踪此信息。

初始化、completer、_listeners

/// Create an initially unbound image stream.
///
/// Once an [ImageStreamCompleter] is available, call [setCompleter].
ImageStream();

/// The completer that has been assigned to this image stream.
///
/// Generally there is no need to deal with the completer directly.
ImageStreamCompleter? get completer => _completer;
ImageStreamCompleter? _completer;

List<ImageStreamListener>? _listeners;

setCompleter

/// Assigns a particular [ImageStreamCompleter] to this [ImageStream].
///
/// This is usually done automatically by the [ImageProvider] that created the
/// [ImageStream].
///
/// This method can only be called once per stream. To have an [ImageStream]
/// represent multiple images over time, assign it a completer that
/// completes several images in succession.
void setCompleter(ImageStreamCompleter value) {
  assert(_completer == null);
  _completer = value;
  if (_listeners != null) {
    final List<ImageStreamListener> initialListeners = _listeners!;
    _listeners = null;
    _completer!._addingInitialListeners = true;
    initialListeners.forEach(_completer!.addListener);
    _completer!._addingInitialListeners = false;
  }
}

setCompleter方法将一个特定的ImageStreamCompleter赋值给ImageStream对象,通常这个操作是由创建ImageStreamImageProvider自动完成的。一个ImageStream对象只能被赋值一次。

if (_listeners != null) { ... }: 如果当前已经有监听器,那么执行以下操作:

  • final List<ImageStreamListener> initialListeners = _listeners!;: 将当前监听器列表存储到initialListeners变量中,并将_listeners设置为null。
  • _completer!._addingInitialListeners = true;: 将_addingInitialListeners设置为true。这个标志是在使用initialListeners添加监听器时使用的,以便能够区分普通添加和初始化添加。
  • initialListeners.forEach(_completer!.addListener);: 将之前存储的监听器逐一添加到_completer中。
  • _completer!._addingInitialListeners = false;: 将_addingInitialListeners设置为false,表示已经完成了初始添加。

addListener

/// Adds a listener callback that is called whenever a new concrete [ImageInfo]
/// object is available. If a concrete image is already available, this object
/// will call the listener synchronously.
///
/// If the assigned [completer] completes multiple images over its lifetime,
/// this listener will fire multiple times.
///
/// {@template flutter.painting.imageStream.addListener}
/// The listener will be passed a flag indicating whether a synchronous call
/// occurred. If the listener is added within a render object paint function,
/// then use this flag to avoid calling [RenderObject.markNeedsPaint] during
/// a paint.
///
/// If a duplicate `listener` is registered N times, then it will be called N
/// times when the image stream completes (whether because a new image is
/// available or because an error occurs). Likewise, to remove all instances
/// of the listener, [removeListener] would need to called N times as well.
///
/// When a `listener` receives an [ImageInfo] object, the `listener` is
/// responsible for disposing of the [ImageInfo.image].
/// {@endtemplate}
void addListener(ImageStreamListener listener) {
  if (_completer != null) {
    return _completer!.addListener(listener);
  }
  _listeners ??= <ImageStreamListener>[];
  _listeners!.add(listener);
}

这段代码是 ImageStream 中的 addListener 方法的实现。它接收一个 ImageStreamListener 参数,并将其添加到 _listeners 列表中,以便在图像加载完成后通知该监听器。如果 _completer 对象已经存在,则直接将监听器添加到 _completer 中。

如果 _completer 对象不存在,则会将监听器添加到 _listeners 列表中。如果 _listeners 列表尚未初始化,则会创建一个新的空列表,并将监听器添加到其中。在图像的 ImageStreamCompleter 对象被设置到 ImageStream 中之前,所有添加到 _listeners 列表中的监听器都会等待,直到图像资源加载完成。

removeListener

/// Stops listening for events from this stream's [ImageStreamCompleter].
///
/// If [listener] has been added multiple times, this removes the _first_
/// instance of the listener.
void removeListener(ImageStreamListener listener) {
  if (_completer != null) {
    return _completer!.removeListener(listener);
  }
  assert(_listeners != null);
  for (int i = 0; i < _listeners!.length; i += 1) {
    if (_listeners![i] == listener) {
      _listeners!.removeAt(i);
      break;
    }
  }
}

该方法用于从ImageStream实例中移除ImageStreamListener。

若该ImageStream实例的_completer已被赋值(即该ImageStream实例已与一个ImageStreamCompleter关联),则该方法将调用与该ImageStreamCompleter关联的removeListener方法,从而移除监听器。否则,该方法将从该ImageStream实例自身的监听器列表中移除指定的监听器,以停止对事件的侦听。

特别强调

ImageStreamCompleter? completer第一次创建的时候,在图片缓存类ImageCache里面,添加了一个监听器

final ImageStreamListener streamListener = ImageStreamListener(listener);
pendingImage = _PendingImage(result, streamListener);
if (trackPendingImage) {
  _pendingImages[key] = pendingImage;
}
// Listener is removed in [_PendingImage.removeListener].
result.addListener(streamListener);

ImageStream stream对象调用stream.setCompleter(completer);时, completer对象内的监听器list里面已经有一个值了。

ImageStream stream对象调用stream.addListener(listener);:

  • 如果completer对象已经初始化了,那么,直接将ImageStreamListener listener对象添加到completer对象的list中。
  • 如果completer对象未初始化,那么,新建一个空数组_listeners, 并将ImageStreamListener listener对象添加到_listeners中, 暂时保存起来。

关键是setCompleter方法和addListener方法哪个先执行的问题。

一般情况下,void setCompleter(ImageStreamCompleter value) 方法会先执行。因为 ImageStreamCompleter 对象通常是在创建 ImageProvider 对象时被创建并赋值给 ImageStream 对象的,而添加监听器的操作是在 ImageStream 对象被使用时才会执行的。不过如果在 ImageStream 对象被创建时已经有监听器被添加,那么在执行 void setCompleter(ImageStreamCompleter value) 方法时,会先把已经添加的监听器添加到 ImageStreamCompleter 对象中。

参考链接

Flutter图片缓存 | Image.network源码分析