源碼:image.dart
圖片的顯示
class Image extends StatefulWidget
Image
繼承自 [StatefulWidget
],它是具有狀態(tài)的猴蹂,通過
@override///image.dart 574L
_ImageState createState() => _ImageState();
可以找到 Image
對應(yīng)的State
類是 _ImageState
桂塞, 那么構(gòu)建Widget的方法就在_ImageState
的build
方法中虑椎,如下:
@override
Widget build(BuildContext context) {
final RawImage image = RawImage(
image: _imageInfo?.image,
width: widget.width,
height: widget.height,
scale: _imageInfo?.scale ?? 1.0,
color: widget.color,
colorBlendMode: widget.colorBlendMode,
fit: widget.fit,
alignment: widget.alignment,
repeat: widget.repeat,
centerSlice: widget.centerSlice,
matchTextDirection: widget.matchTextDirection,
invertColors: _invertColors,
filterQuality: widget.filterQuality,
);
if (widget.excludeFromSemantics)
return image;
return Semantics(
container: widget.semanticLabel != null,
image: true,
label: widget.semanticLabel == null ? '' : widget.semanticLabel,
child: image,
);
}
由源碼可以看到瘟芝,在此方法中創(chuàng)建的是 RawImage
widget ,傳入 imageInfo.image
覆醇,并由 RawImage
來渲染圖片數(shù)據(jù)绅喉。
圖片的加載
Image
類有這么幾個構(gòu)造方法,方便開發(fā)者加載顯示本地叫乌,文件,網(wǎng)絡(luò)中的圖片數(shù)據(jù)徽缚。
/// * [new Image], for obtaining an image from an [ImageProvider].
/// * [new Image.asset], for obtaining an image from an [AssetBundle]
/// using a key.
/// * [new Image.network], for obtaining an image from a URL.
/// * [new Image.file], for obtaining an image from a [File].
/// * [new Image.memory], for obtaining an image from a [Uint8List].
Image
加載了圖片數(shù)據(jù)后存儲在 imageInfo.image
憨奸。ImageInfo
類很簡單,只有兩個屬性:
@immutable
class ImageInfo {
//圖片像素點陣凿试,
final ui.Image image;
//縮放比例排宰,例當(dāng)scale為2時,寬高都將變?yōu)樵瓐D的2倍那婉。
final double scale;
}
那imageInfo
又是在哪生成的呢板甘,也就是在哪加載了圖片的數(shù)據(jù)呢?根據(jù) ImageInfo
類的注釋详炬, ImageInfo
一旦被獲得盐类,就會被 ImageStream
用來表示為真實的圖片數(shù)據(jù)。
ImageStream
的部分源碼如下:
class ImageStream extends Diagnosticable {
/// 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;
可見 ImageStream
主要是由 ImageStreamCompleter
來提供支持呛谜,只是一個 ImageStreamCompleter
的包裝類在跳,不過當(dāng)ImageStreamCompleter
可用的時候,需調(diào)用ImageStream.setCompleter
方法隐岛,以將事件傳遞給ImageStream
中的監(jiān)聽者猫妙。
那么 ImageStreamCompleter
又是個啥?繼續(xù)往下看聚凹,源碼:
/// Base class for those that manage the loading of [dart:ui.Image] objects for
/// [ImageStream]s.
///
/// [ImageStreamListener] objects are rarely constructed directly. Generally, an
/// [ImageProvider] subclass will return an [ImageStream] and automatically
/// configure it with the right [ImageStreamCompleter] when possible.
abstract class ImageStreamCompleter extends Diagnosticable {
final List<_ImageListenerPair> _listeners = <_ImageListenerPair>[];
ImageInfo _currentImage;
void addListener(ImageListener listener, { ImageErrorListener onError }) {
//省略
}
void removeListener(ImageListener listener) {
//省略
}
/// Calls all the registered listeners to notify them of a new image.
@protected
void setImage(ImageInfo image) {
_currentImage = image;
if (_listeners.isEmpty)
return;
final List<ImageListener> localListeners = _listeners.map<ImageListener>(
(_ImageListenerPair listenerPair) => listenerPair.listener
).toList();
for (ImageListener listener in localListeners) {
try {
listener(image, false);
} catch (exception, stack) {
reportError(
context: 'by an image listener',
exception: exception,
stack: stack,
);
}
}
}
}
ImageStreamCompleter
是一個抽象類割坠。去掉添加/移除Listener的方法后,還剩一個 [setImage
] 方法彼哼,方法內(nèi)部邏輯很簡單对妄,將傳入的參數(shù) ImageInfo
傳遞到各個 ImageListener
,然后刷新GUI沪羔。
ImageStreamCompleter
有兩個實現(xiàn)類饥伊,分別為
OneFrameImageStreamCompleter
MultiFrameImageStreamCompleter
。
那剛才看下來的源碼只是一個監(jiān)聽的設(shè)計而已:
widget
監(jiān)聽 ImageStream
, 而widget
設(shè)置給ImageStream
的 listener
被傳遞到 ImageStreamCompleter
蔫饰。當(dāng)圖片成功加載時琅豆,ImageStreamCompleter
的setImage
方法被調(diào)用,圖片通過回調(diào)回傳到 widget
篓吁。
圖片的加載2
Image類構(gòu)造方法需傳入一個 ImageProvider
茫因,圖片應(yīng)便是在這里面被加載的:
abstract class ImageProvider<T> {
///根據(jù) configuration 處理 ImagePrivoder,并返回一個 ImageStream對象
///子類應(yīng)該實現(xiàn) [obtainKey] 和 [load] 方法,并且這兩個方法在此流程中使用
ImageStream resolve(ImageConfiguration configuration) {
assert(configuration != null);
final ImageStream stream = ImageStream();
T obtainedKey;
obtainKey(configuration).then<void>((T key) {
obtainedKey = key;
///當(dāng)拿到KEY時杖剪,查詢緩存
stream.setCompleter(PaintingBinding.instance.imageCache.putIfAbsent(key, () => load(key)));
}).catchError(
//省略錯誤處理...
return null;
}
);
return stream;
}
/// 根據(jù) ImageConfiguration和 ImageProvider 的屬性來生成一個KEY用來標(biāo)識加載的圖片
/// KEY要求實現(xiàn) '==' 和 hascode 方法冻押,這個KEY主要是用于緩存
@protected
Future<T> obtainKey(ImageConfiguration configuration);
///開始加載圖片
@protected
ImageStreamCompleter load(T key);
}
其中 resolve
方法根據(jù)ImageConfiguration
來獲取相應(yīng)的ImageStream
。
到目前為止盛嘿,圖片獲取的流程應(yīng)該差不多可以這樣來表示...
圖片的緩存
在ImageProvider
的源碼中能過看到洛巢,圖片的加載是做過緩存處理的。即在ImageProvider
的resolve
方法中次兆,有這么一句:
stream.setCompleter(PaintingBinding.instance.imageCache.putIfAbsent(key, () => load(key)));
1. KEY
image.putIfAbsent
傳入的 key 的類型即為ImageProvider<T>
中的T
稿茉,需是不可改變的(immutable)以及實現(xiàn)[==
] 和 [hashcode
]方法。并且由 ImageProvider.obtainKey
方法生成芥炭,例如NetworkImage
中是這么實現(xiàn)的:
class NetworkImage extends ImageProvider<NetworkImage> {
@override
Future<NetworkImage> obtainKey(ImageConfiguration configuration) {
return SynchronousFuture<NetworkImage>(this);
}
@override
bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType)
return false;
final NetworkImage typedOther = other;
return url == typedOther.url
&& scale == typedOther.scale;
}
@override
int get hashCode => hashValues(url, scale);
}
2.ImageCache
緩存的邏輯主要在 putIfAbent
方法中
使用的 LRU漓库,并且默認(rèn)最多存儲 1000個緩存,最大緩存限制為100MiB
const int _kDefaultSize = 1000;
const int _kDefaultSizeBytes = 100 << 20; // 100 MiB
目前的圖片大小是這么計算的:
final int imageSize = info?.image == null ? 0 : info.image.height * info.image.width * 4;
3.緩存
由上代碼可以看出园蝠,flutter 自帶的緩存只會在運行期間生效渺蒿,也就是緩存在內(nèi)存中。