|
@@ -110,11 +110,165 @@ dispatch_main_async_safe(block) 保证 block 在主线程中执行,其中包
|
|
|
注意 SD+FLAnimatedImageView加载GIF时并没有对图片缓存,每次都需要重新去请求图片
|
|
|
|
|
|
#### SDWebImage5.x
|
|
|
-
|
|
|
新推出 SDAnimatedImageView类
|
|
|
+整体流程
|
|
|
+
|
|
|
+1、SDAnimatedImageView + WebCache.m
|
|
|
+[SDAnimatedImageView sd_setImageWithURL:]
|
|
|
+
|
|
|
+[SDAnimatedImageView sd_internalSetImageWithURL:]
|
|
|
+
|
|
|
+2、 [UIView sd_internalSetImageWithURL:]
|
|
|
+
|
|
|
+3、接着交给SDWebImageManager分发
|
|
|
+
|
|
|
+[SDWebImageManager loadImageWithURL:]
|
|
|
+
|
|
|
+3.1 首先从缓存获取
|
|
|
+[SDWebImageManager callCacheProcessForOperation:]
|
|
|
+
|
|
|
+4、缓存查询流程
|
|
|
+[SDImageCache queryImageForKey:] 依次从内存缓存和磁盘缓存去查询
|
|
|
+
|
|
|
+4.1 如果内存缓存存在,直接返回image
|
|
|
+
|
|
|
+4.2 如果内存缓存没有,但磁盘缓存存在
|
|
|
+
|
|
|
+ NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
|
|
|
+ UIImage *diskImage;
|
|
|
+ SDImageCacheType cacheType = SDImageCacheTypeNone;
|
|
|
+ if (image) {
|
|
|
+ // the image is from in-memory cache, but need image data
|
|
|
+ diskImage = image;
|
|
|
+ cacheType = SDImageCacheTypeMemory;
|
|
|
+ } else if (diskData) {
|
|
|
+ cacheType = SDImageCacheTypeDisk;
|
|
|
+ // decode image data only if in-memory cache missed
|
|
|
+ diskImage = [self diskImageForKey:key data:diskData options:options context:context];
|
|
|
+ if (diskImage && self.config.shouldCacheImagesInMemory) {
|
|
|
+ NSUInteger cost = diskImage.sd_memoryCost;
|
|
|
+ [self.memoryCache setObject:diskImage forKey:key cost:cost];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+只有当内存缓存没命中时才会对磁盘缓存的data解码
|
|
|
+
|
|
|
+调用 SDImageCacheDefine.m里的SDImageCacheDecodeImageData进行解码
|
|
|
+
|
|
|
+ UIImage *image = SDImageCacheDecodeImageData(data, key, [[self class] imageOptionsFromCacheOptions:options], context);
|
|
|
+
|
|
|
+这里有判断是否只需要解码第一帧,如果不仅仅解码首帧,则从 context中取出AnimatedImageClass,默认使用的是 SDAnimatedImage,然后进行解码,根据需要还可以预解码所有帧。
|
|
|
+
|
|
|
+如果是一个动图,并且不只解码第一帧,那么就会调用 SDAnimatedImage 的初始化方法把所有帧都解码出来。
|
|
|
+[SDAnimatedImage initWithData] 传入data
|
|
|
+
|
|
|
+通过SDImageCodersManager找到能对当前data解码的coder类(遵循SDAnimatedImageCoder协议),创建coder实例 ,最后调用[self initWithAnimatedCoder:animatedCoder scale:scale];生成SDAnimatedImage对象,这个对象保存_animatedCoder和_animatedImageFormat。
|
|
|
+
|
|
|
+```
|
|
|
+ - (instancetype)initWithAnimatedCoder:(id<SDAnimatedImageCoder>)animatedCoder scale:(CGFloat)scale {
|
|
|
+ if (!animatedCoder) {
|
|
|
+ return nil;
|
|
|
+ }
|
|
|
+ UIImage *image = [animatedCoder animatedImageFrameAtIndex:0];
|
|
|
+ if (!image) {
|
|
|
+ return nil;
|
|
|
+ }
|
|
|
+ self = [super initWithCGImage:image.CGImage scale:MAX(scale, 1) orientation:image.imageOrientation];
|
|
|
+ if (self) {
|
|
|
+ // Only keep the animated coder if frame count > 1, save RAM usage for non-animated image format (APNG/WebP)
|
|
|
+ if (animatedCoder.animatedImageFrameCount > 1) {
|
|
|
+ _animatedCoder = animatedCoder;
|
|
|
+ }
|
|
|
+ NSData *data = [animatedCoder animatedImageData];
|
|
|
+ SDImageFormat format = [NSData sd_imageFormatForImageData:data];
|
|
|
+ _animatedImageFormat = format;
|
|
|
+ }
|
|
|
+ return self;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+SDAnimatedImage本身也是UIImage,实际上是第一帧图。
|
|
|
+
|
|
|
+磁盘缓存返回后同步内存缓存,memoryCache
|
|
|
+
|
|
|
+ diskImage = [self diskImageForKey:key data:diskData options:options context:context];
|
|
|
+ if (diskImage && self.config.shouldCacheImagesInMemory) {
|
|
|
+ NSUInteger cost = diskImage.sd_memoryCost;
|
|
|
+ [self.memoryCache setObject:diskImage forKey:key cost:cost];
|
|
|
+ }
|
|
|
+
|
|
|
+ 所以内存缓存存储的是SDAnimatedImage对象
|
|
|
+
|
|
|
+
|
|
|
+5、缓存如果命中返回,如果没有则进行下载操作
|
|
|
+[SDWebImageManager callDownloadProcessForOperation:]
|
|
|
+
|
|
|
+6、下载结束同样地调用 SDImageLoaderDecodeImageData 进行解码,在self.coderQueue 队列里进行
|
|
|
+
|
|
|
+ [self.coderQueue cancelAllOperations];
|
|
|
+ [self.coderQueue addOperationWithBlock:^{
|
|
|
+ UIImage *image = SDImageLoaderDecodeImageData(imageData, self.request.URL, [[self class] imageOptionsFromDownloaderOptions:self.options], self.context);
|
|
|
+ CGSize imageSize = image.size;
|
|
|
+ if (imageSize.width == 0 || imageSize.height == 0) {
|
|
|
+ [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorBadImageData userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}]];
|
|
|
+ } else {
|
|
|
+ [self callCompletionBlocksWithImage:image imageData:imageData error:nil finished:YES];
|
|
|
+ }
|
|
|
+ [self done];
|
|
|
+ }];
|
|
|
+
|
|
|
+
|
|
|
+7、下载结束缓存处理
|
|
|
+回到SDWebImageManager [imageCache storeImage:image imageData:data forKey:]
|
|
|
+依次按需存入内存缓存和磁盘缓存
|
|
|
+
|
|
|
+存储时有时候(只有image没有data时)会先对image指定图片格式信息,类似打标签
|
|
|
+
|
|
|
+ SDImageFormat format = image.sd_imageFormat;
|
|
|
+ if (format == SDImageFormatUndefined) {
|
|
|
+ // If image is animated, use GIF (APNG may be better, but has bugs before macOS 10.14)
|
|
|
+ if (image.sd_isAnimated) {
|
|
|
+ format = SDImageFormatGIF;
|
|
|
+ } else {
|
|
|
+ // If we do not have any data to detect image format, check whether it contains alpha channel to use PNG or JPEG format
|
|
|
+ if ([SDImageCoderHelper CGImageContainsAlpha:image.CGImage]) {
|
|
|
+ format = SDImageFormatPNG;
|
|
|
+ } else {
|
|
|
+ format = SDImageFormatJPEG;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+再调用
|
|
|
+
|
|
|
+`data = [[SDImageCodersManager sharedManager] encodedDataWithImage:image format:format options:nil];`
|
|
|
+
|
|
|
+根据不同的format将image编码,根据对应的格式,交给指定的encode类去进行压缩处理。最后调用存储到磁盘
|
|
|
+
|
|
|
+`[self.diskCache setData:imageData forKey:key];`
|
|
|
+
|
|
|
+这种情况下,下次从磁盘读取时可以知道图片的格式,之前版本的SD并没有判断GIF,导致存到磁盘的可能是PNG,自然下次读取时也不会播放。
|
|
|
+
|
|
|
+8、展示GIF
|
|
|
+
|
|
|
+UIView+WebCore finalSetImageBlock
|
|
|
+
|
|
|
+ UIImageView *imageView = (UIImageView *)view;
|
|
|
+ finalSetImageBlock = ^(UIImage *setImage, NSData *setImageData, SDImageCacheType setCacheType, NSURL *setImageURL) {
|
|
|
+ imageView.image = setImage;
|
|
|
+ };
|
|
|
+
|
|
|
+[SDAnimatedImageView setImage:]
|
|
|
+调 startAnimating 开始播放
|
|
|
|
|
|
|
|
|
+几个关键的类和方法
|
|
|
|
|
|
+SDAnimatedImagePlayer
|
|
|
|
|
|
+SDDisplayLink
|
|
|
|
|
|
+iOS上使用CADisplayLink,来自定义循环播放动画
|
|
|
|
|
|
+calculateMaxBufferCount
|