iOS原生UITableView滚动中图片取消下载
iOS原生图片库通常使用了SDWebImage,而UITableView的复用机制是创建一堆UITableViewCell,在滚动中不断地用新数据填充这些UITableViewCell;而SDWebImage实现的UIView+WebCacheOperation.m中,如果一个UIView已经存在了下载中的网络请求,那么就会取消掉这个网络请求:
- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {
if (key) {
// Cancel in progress downloader from queue
SDOperationsDictionary *operationDictionary = [self sd_operationDictionary];
id operation;
@synchronized (self) {
operation = [operationDictionary objectForKey:key];
}
if (operation) {
if ([operation conformsToProtocol:@protocol(SDWebImageOperation)]) {
[operation cancel];
}
@synchronized (self) {
[operationDictionary removeObjectForKey:key];
}
}
}
}
所以UITableViewCell中使用SDWebImage的图片视图会在滚动中被复用从而取消下载,实现了滚动中取消图片下载的功能。
ReactNative中FlatList滚动中图片取消下载
ReactNative的FlatList是依赖DOM和key实现的各种优化,当FlatList中的elements每个都有不同的key时,滚动之后再次render的时候,出现的相同的key的element就不需要mutate,而上次显示的element这次没有显示则会从DOM中移除,在iOS原生反馈出来的就是这个element对应的RCTView从内存中被释放。
FlatList继承自VirtualizedList,VirtualizedList在滚动中移除不显示cell的流程如下图所示:
那么取消滚动中超出屏幕范围的cell的图片下载就可以在cell的componentWillUnmount中来实现。项目中使用了react-native-img-cache做图片下载缓存,而react-native-img-cache同时提供了cancel方法来取消图片的下载:
ImageCache.get().cancel("https://i.ytimg.com/vi/yaqe1qesQ8c/maxresdefault.jpg");
FlatList的windowSize属性
FlatList的windowSize属性表示同时显示几屏,默认值是21,也就是当前屏,加上上10屏和下10屏。在实际中建议设置为3,即可以适当地移除显示屏幕范围之外的cell,来取消这些cell中图片的下载。
/**
* Determines the maximum number of items rendered outside of the visible area, in units of
* visible lengths. So if your list fills the screen, then `windowSize={21}` (the default) will
* render the visible screen area plus up to 10 screens above and 10 below the viewport. Reducing
* this number will reduce memory consumption and may improve performance, but will increase the
* chance that fast scrolling may reveal momentary blank areas of unrendered content.
*/
FlatList的initialNumToRender属性
FlatList的initialNumToRender属性表示一开始要显示几个cell,建议根据实际情况设置为cell能够覆盖一屏的数量,该值默认为10。
/**
* How many items to render in the initial batch. This should be enough to fill the screen but not
* much more. Note these items will never be unmounted as part of the windowed rendering in order
* to improve perceived performance of scroll-to-top actions.
*/
测试
为了方便观察cell超过windowSize就会被移除,实验中设置windowSize为,同时设置initialNumToRender为4,测试代码如下:
import {CachedImage, ImageCache} from "react-native-img-cache";
class ImageTT extends React.Component {
componentWillUnmount() {
ImageCache.get().cancel(this.props.imageUrl);
}
render() {
return (
);
}
}
export default function App() {
return (
}
keyExtractor={item => item.id}
windowSize={1}
initialNumToRender={4}
/>
);
}
测试数据中url先试用伪造的url,以便于观察取消下载的调用情况,下面是componentWillUnmount中的断点信息在滚动中在控制台的打印信息:
伪造的url数据时从“https://www.abc.com/1.jpg”开始的,在图片上滚动之后的取消下载却是从“https://www.abc.com/5.jpg”开始的,这是由于我们设置的initialNumToRender为4,所以头4个cell永远不会被移除,所以是从“https://www.abc.com/5.jpg”开始移除;这也方便了“Go to top”这个功能。
将数据换为实际的图片URL进行测试结果如下图所示:
其中红色部分是一开始的4个cell对应的图片下载,随着滚动后续cell中的图片取消了下载,但是最开始的4个下载一直没有受到影响,而最下面的下载则是在windowSize范围之内的cell的下载。
总结
- 实现ReactNative在滚动中取消下载依靠FlatList的移除cell的机制,在cell被移除时添加取消对应图片下载操作来完成这一功能。
- 如果使用了react-native-img-cache,那么可以在cell的componentWillUnmount中使用react-native-img-cache提供的cancel方法来取消下载。
- 如果使用了react-native-fast-image,那么可以在iOS原生端扩展对应的RCTView在dealloc时取消对应url下载。
- 结合项目实际情况调整FlatList的windowSize和initialNumToRender属性。