Flutter: には画像コンポーネントがありImage
、通常はそのImage.network(src)
, Image.file(src)
,を使用してImage.asset(src)
画像を読み込みます。の一般的なコンストラクターは
次のとおりです。Image
const Image({
super.key,
required this.image,
this.frameBuilder,
this.loadingBuilder,
this.errorBuilder,
this.semanticLabel,
this.excludeFromSemantics = false,
this.width,
this.height,
this.color,
this.opacity,
this.colorBlendMode,
this.fit,
this.alignment = Alignment.center,
this.repeat = ImageRepeat.noRepeat,
this.centerSlice,
this.matchTextDirection = false,
this.gaplessPlayback = false,
this.isAntiAlias = false,
this.filterQuality = FilterQuality.low,
})
その構築方法から、Image コンポーネントには ImageProvider 型の必須パラメーター image があることがわかります。ImageProvider は、画像データの取得と読み込みに関連するインターフェイスを定義する抽象クラスです。これには 2 つの主な責任があります。
- 1. 画像データソースを提供します。
- 2. 画像をキャッシュします。
ImageProvider
抽象クラス:
abstract class ImageProvider<T extends Object> {
const ImageProvider();
ImageStream resolve(ImageConfiguration configuration) {
...
}
ImageStream createStream(ImageConfiguration configuration) {
return ImageStream();
}
void resolveStreamForKey(ImageConfiguration configuration, ImageStream stream, T key, ImageErrorListener handleError) {
...
}
Future<bool> evict({
ImageCache? cache, ImageConfiguration configuration = ImageConfiguration.empty }) async {
...
}
Future<T> obtainKey(ImageConfiguration configuration);
}
上記のソース コードから、画像の読み込みと解析が 、特にそのサブクラスImageProvider
によって。class やclassImageProvider
など、多くのサブクラスが派生し、ネットワークから画像データをロードしたり、インストール パッケージ内のリソース ファイルからロードしたりします。NetworkImage
AssetImage
NetworkImage
AssetImage
画像読み込み
画像データ ソースをロードするためのインターフェイスである ImageProvider にはメソッドが提供されておりload()
、異なるデータ ソースは異なる方法でロードされます。
ネットワーク イメージの読み込みが使用されImage.network()
、対応する ImageProvider はNetworkImage
、load() メソッドを実装するクラスです。
ImageStreamCompleter load(FileImage key, DecoderCallback decode) {
return MultiFrameImageStreamCompleter(
codec: _loadAsync(key, null, decode),
scale: key.scale,
debugLabel: key.file.path,
informationCollector: () => <DiagnosticsNode>[
ErrorDescription('Path: ${
file.path}'),
],
);
}
- ロード メソッドの戻り値の型は ImageStreamCompleter で、画像の読み込みプロセスを管理するためのいくつかのインターフェイスを定義する抽象クラスです。画像ウィジェットはこれを使用して画像の読み込みステータスを監視します。
- MultiFrameImageStreamCompleter は ImageStreamCompleter のサブクラスであり、このクラスを実装すると、ImageStreamCompleter インスタンスをすばやく作成できます。
MultiFrameImageSteamCompleter には、ソース コードでメソッドを呼び出すために使用される codec パラメーターがあり_loadAsync()
、メソッドの実装は次のとおりです。
Future<ui.Codec> _loadAsync(
NetworkImage key,
StreamController<ImageChunkEvent> chunkEvents,
image_provider.DecoderBufferCallback? decode,
image_provider.DecoderCallback? decodeDepreacted,
) async {
try {
final Uri resolved = Uri.base.resolve(key.url);
final HttpClientRequest request = await _httpClient.getUrl(resolved);
headers?.forEach((String name, String value) {
request.headers.add(name, value);
});
final HttpClientResponse response = await request.close();
if (response.statusCode != HttpStatus.ok) {
await response.drain<List<int>>(<int>[]);
throw image_provider.NetworkImageLoadException(statusCode: response.statusCode, uri: resolved);
}
final Uint8List bytes = await consolidateHttpClientResponseBytes(
response,
onBytesReceived: (int cumulative, int? total) {
chunkEvents.add(ImageChunkEvent(
cumulativeBytesLoaded: cumulative,
expectedTotalBytes: total,
));
},
);
...
if (decode != null) {
final ui.ImmutableBuffer buffer = await ui.ImmutableBuffer.fromUint8List(bytes);
return decode(buffer);
} else {
assert(decodeDepreacted != null);
return decodeDepreacted!(bytes);
}
}
_loadAsync()
ソース コードから、このメソッドが画像のダウンロードを担当し、decode()
ダウンロードされた画像データをデコードするメソッドを呼び出していることがわかります。
画像キャッシュ
画像キャッシュの主要な方法は次のとおりです:obtainKey(ImageConfiguration)
この方法は主に画像キャッシュの実現と連携します. ImageProvider がデータ ソースからデータをロードした後, 画像データをグローバル ImageCache にキャッシュします, そして画像データ キャッシュは Map, Map のキーは、このメソッドを呼び出したときの戻り値であり、異なるキーは異なる画像データ キャッシュを表します。
resolve
このメソッドは、ImageProvider
Image に公開されるメインのエントリ メソッドであり、ImageConfiguration パラメータを受け取り、ImageStream を返します。
ImageStream resolve(ImageConfiguration configuration) {
assert(configuration != null);
final ImageStream stream = createStream(configuration);
_createErrorHandlerAndKey(
configuration,
(T key, ImageErrorListener errorHandler) {
resolveStreamForKey(configuration, stream, key, errorHandler);
},
...
);
return stream;
}
ImageConfiguration: イメージとデバイスに関する情報が含まれます。_createErrorHandlerAndKey
キーをロードし、エラー ハンドラーを作成するために内部的に呼び出されます。
resolveStreamForKey
方法:
void resolveStreamForKey(ImageConfiguration configuration, ImageStream stream, T key, ImageErrorListener handleError) {
if (stream.completer != null) {
//缓存逻辑
final ImageStreamCompleter? completer = PaintingBinding.instance.imageCache.putIfAbsent(
key,
() => stream.completer!,
onError: handleError,
);
assert(identical(completer, stream.completer));
return;
}
//缓存逻辑
final ImageStreamCompleter? completer = PaintingBinding.instance.imageCache.putIfAbsent(
key,
() => loadBuffer(key, PaintingBinding.instance.instantiateImageCodecFromBuffer),
onError: handleError,
);
if (completer != null) {
stream.setCompleter(completer);
}
}
solve メソッドでは、PaintingBinding.instance.imageCache
PaintingBinding のプロパティである ImageCache のインスタンスであるsolveStreamForKey が呼び出されます。PaintingBinding.instance と imageCache は両方ともシングルトンであるため、イメージ キャッシュはグローバルであり、PaintingBinding.instance.imageCache
均一に
ImageCache キャッシュの具体的な実装
ImageCache の定義:
const int _kDefaultSize = 1000;
const int _kDefaultSizeBytes = 100 << 20; // 100 MiB
class ImageCache {
final Map<Object, _PendingImage> _pendingImages = <Object, _PendingImage>{
};
final Map<Object, _CachedImage> _cache = <Object, _CachedImage>{
};
final Map<Object, _LiveImage> _liveImages = <Object, _LiveImage>{
};
int get maximumSize => _maximumSize;
int _maximumSize = _kDefaultSize;
...
}
ImageCache には 3 つのキャッシュ プールがあります。
- _pendingImages: ロードおよびデコード中の画像を保存するために使用されます。画像がロードおよびデコードされると、ImageCache は _pendingImages の対応するエントリを自動的に削除します。
- _cache: 読み込まれたすべての画像を保存するために使用されます。画像キャッシュの数とメモリ使用量が ImageCache の上限を超えない場合、_cache は常にキャッシュ エントリを保持し、超えた場合は LRU に従って解放されます。
- _liveImages: 使用中の画像を保存するために使用されます。画像ウィジェットが画像を削除または置換するか、画像ウィジェット自体が削除されると、ImageCache は対応するエントリを _liveImages から削除します。ImageCache のみがすべてのキャッシュ プール エントリから同じ画像を解放します。画像は実際に解放されます
。記憶の中で。
画像キャッシュのキーの生成方法
Map 内の同じキーの値は上書きされるため、つまりキーは画像キャッシュの一意の識別子であり、キーが異なる限り画像データはキャッシュ内に分散されます。では、画像の一意の識別子は何でしょうか? メソッドはソースコードから確認できますImageProvider.obtainKey()
が、画像キャッシュで使用するキーはこのメソッドで生成されており、ImageProviderのサブクラスがこのメソッドを書き換えています。
これは、異なる ImageProvider が異なるキーを定義することを意味します。NetworkImage の getKey() メソッド:
Future<NetworkImage> obtainKey(image_provider.ImageConfiguration configuration) {
return SynchronousFuture<NetworkImage>(this);
}
SynchronousFuture を作成し、それ自体を返すため、キーを比較するときは==
演算子だけを確認してください。
operator ==(Object other) {
if (other.runtimeType != runtimeType) {
return false;
}
return other is NetworkImage && other.url == url && other.scale == scale;
}
bool
ネットワーク画像の場合、キーは実際には URL+スケールです。したがって、2 つの画像の URL と縮尺は同じであるため、1 つのコピーのみがメモリにキャッシュされます。
キャッシュサイズを設定する
ImageCache クラスにはデフォルトのキャッシュ サイズがあります。
const int _kDefaultSize = 1000;//最多1000张
const int _kDefaultSizeBytes = 100 << 20; //最大 100 MiB
次のコードを使用して、カスタム キャッシュ制限を設定することもできます。
PaintingBinding.instance.imageCache.maximumSize=500; //最多500张
PaintingBinding.instance.imageCache.maximumSizeBytes = 50 << 20; //最大50M