關于圖片加載,Flutter本身提供的Image Widget已經實現了加載網絡圖片的功能,且具備內存緩存的機制,我們看看Image的網絡圖片加載的實現。
## Image的構造
### 有參構造函數
~~~
Image(Key key, @required this.image, …)
~~~
開發者可根據自定義的ImageProvider來創建Image。
### 命名構造函數
~~~
Image.network(String src, …)
~~~
src即是根據網絡獲取的圖片url地址。
~~~
Image.file(File file, …)
~~~
file指本地一個圖片文件對象,安卓中需要android.permission.READ_EXTERNAL_STORAGE權限。
~~~
Image.asset(String name, …)
~~~
name指項目中添加的圖片資源名,事先在pubspec.yaml文件中有聲明。
~~~
Image.memory(Uint8List bytes, …)
~~~
bytes指內存中的圖片數據,將其轉化為圖片對象。
## ImageProvider
`ImageProvider`一個抽象類,意思是圖片的提供者,主要是為Image控件提供圖片加載的邏輯,Image的有參構造函數里的image就是需要ImageProvider。
它的子類包括`NetworkImage`、`FileImage`、`ExactAssetImage`、`AssetImage`、`MemoryImage`等,網絡加載圖片使用的就是`NetworkImage`。
## Image.network源碼分析
Image作為一個StatefulWidget其狀態由_ImageState控制,_ImageState繼承自State類,其生命周期方法包括initState()、didChangeDependencies()、build()、deactivate()、dispose()、didUpdateWidget()等。我們重點來_ImageState中函數的執行
### _ImageState源碼分析
~~~
@override
void didChangeDependencies() {
_updateInvertColors();
_resolveImage();
if (TickerMode.of(context))
_listenToStream();
else
_stopListeningToStream();
super.didChangeDependencies();
}
~~~
#### _resolveImage
`_updateInvertColors`就是設置了_invertColors的值,重點看看`_resolveImage`
~~~
void _resolveImage() {
final ImageStream newStream =
widget.image.resolve(createLocalImageConfiguration(
context,
size: widget.width != null && widget.height != null ? Size(widget.width, widget.height) : null,
));
_updateSourceStream(newStream);
}
void _updateSourceStream(ImageStream newStream) {
if (_imageStream?.key == newStream?.key)
return;
if (_isListeningToStream)
_imageStream.removeListener(_getListener());
if (!widget.gaplessPlayback)
setState(() { _imageInfo = null; });
setState(() {
_loadingProgress = null;
_frameNumber = null;
_wasSynchronouslyLoaded = false;
});
_imageStream = newStream;
if (_isListeningToStream)
_imageStream.addListener(_getListener());
}
~~~
創建了一個`ImageStream`對象,該對象是一個圖片資源的句柄,其持有著圖片資源加載完畢后的監聽回調和圖片資源的管理者。
由于剛初始化_isListeningToStream為false,因此_updateSourceStream做的事情主要是把_imageStream設置成新創建的ImageStream。
#### _listenToStream
接著分析didChangeDependencies,后面會判斷TickerMode.of(context)的值,這個值默認是true,和AnimationConrol有關,后續可以深入研究。然后調用\_listenToStream()。
~~~
void _listenToStream() {
if (_isListeningToStream)
return;
_imageStream.addListener(_getListener());
_isListeningToStream = true;
}
ImageStreamListener _getListener([ImageLoadingBuilder loadingBuilder]) {
loadingBuilder ??= widget.loadingBuilder;
return ImageStreamListener(
_handleImageFrame,
onChunk: loadingBuilder == null ? null : _handleImageChunk,
);
}
void _handleImageFrame(ImageInfo imageInfo, bool synchronousCall) {
setState(() {
_imageInfo = imageInfo;
_loadingProgress = null;
_frameNumber = _frameNumber == null ? 0 : _frameNumber + 1;
_wasSynchronouslyLoaded |= synchronousCall;
});
}
~~~
這里主要是向ImageStream對象中添加了監聽器\_handleImageChanged,監聽方法如下監聽方法最終回調用`setState`方法來通知界面刷新。
### ImageProvider源碼分析
`ImageStream`對象是通過`widget.image.resolve`方法創建的,也就是對應`NetworkImage`的`resolve`方法,實現在`ImageProvider`類中找到了。
#### resolve
~~~
ImageStream resolve(ImageConfiguration configuration) {
assert(configuration != null);
final ImageStream stream = ImageStream();
T obtainedKey;
final Zone dangerZone = Zone.current.fork(
specification: ZoneSpecification(
handleUncaughtError: (Zone zone, ZoneDelegate delegate, Zone parent, Object error, StackTrace stackTrace) {
handleError(error, stackTrace);
}
)
);
dangerZone.runGuarded(() {
Future<T> key;
try {
key = obtainKey(configuration);
} catch (error, stackTrace) {
handleError(error, stackTrace);
return;
}
key.then<void>((T key) {
obtainedKey = key;
final ImageStreamCompleter completer = PaintingBinding.instance
.imageCache.putIfAbsent(key, () => load(key), onError: handleError);
if (completer != null) {
stream.setCompleter(completer);
}
}).catchError(handleError);
});
return stream;
}
~~~
`ImageStreamCompleter`對象就是圖片資源的一個管理類。
ImageStream中的圖片管理者ImageStreamCompleter通過`PaintingBinding.instance.imageCache.putIfAbsent(key, () => load(key), onError: handleError);`方法創建,imageCache是Flutter框架中實現的用于圖片緩存的單例,查看其中的`putIfAbsent`方法
。
## QA
### ImageProvider是啥
## 參考資料
[Flutter Image全解析](https://www.jianshu.com/p/68879dd00f81)(Image詳細參數說明)
[Flutter中網絡圖片加載和緩存](https://blog.csdn.net/weixin_43499085/article/details/88842438)(原理說明)