0x0背景
原本是放到自己博客的,不怎么用了,把文章同步过来,原文地址[iOS/OC]SDWebImage和LKImage对比
LKImageKit是腾讯开源的一个高性能图片加载框架,虽然第一时间下载了源码,但是只是简单的看了框架,没有细致的研读源码。最近空闲下来,学习了一下LKImageKit源码,其中有很多巧妙的实现。本文将通过1张图片的加载流程,对比两个图片框架。
0x1正文
LKImageKit和SDWebImage分别选用了两种不同的图片加载方案。LKImageKit的图片加载是提供了一个LKImageView,加载图片需要使用指定的容器。SDWebImage是写了一个Category,加载图片可以使用系统的UIImageView或其子类。流程上。
1.入口
发起一个图片加载,LKImageKit是在layoutSubviews时,发起1次图片加载,SDWebImage提供了1个主动发起图片加载请求的接口sd_setImageWithUrl
LKImageKit发起图片加载:
// 上层调用
imageView.URL = [NSURL urlWithString:@"https://xxx.png"];
imageView.request.synchronized = YES;
// 底层加载
- (void)layoutSubviews {
[super layoutSubviews];
[self layoutAndLoad];
}
SDWebImage发起图片加载
// 上层调用
[imageView sd_setImageWithURL:[NSURL URLWithString:@""]];
// 之后开始下载流程
两个图片库都提供了加载回调,不同的是,LKImageKit提供的是delegate的方法,SDWebImage提供的是callback
LKImageKit
@protocol LKImageViewDelegate
@optional
- (void)LKImageViewImageLoading:(LKImageView *)imageView request:(LKImageRequest *)request;
- (void)LKImageViewImageDidLoad:(LKImageView *)imageView request:(LKImageRequest *)request;
@end
SDWebImage
typedef void(^SDWebImageCompletionBlock)(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL);
2.下载
LKImageKit进行图片下载会统一由LKImageManger进行管理,LKImageKit提供的请求合并的优化就是通过LKImageManager进行的。请求会通过blockOperation放到1个operationQueue中进行多个加载请求的队列管理。
资源加载则由LKImageLoader进行,由单例LKImageLoaderManager进行管理。网络下载是LKImageNetworkFileLoader进行,使用了NSURLSession,本地图片加载通过LKImageLocalFileLoader进行。
[requestLV2.loader dataWithRequest:requestLV2
callback:^(LKImageRequest *requestLV2, NSData *data, float progress, NSError *error) {
[self loadDataRequestFinished:requestLV2 data:data progress:progress error:error];
}];
SDWebImage进行图片下载由SDWebImageManager进行管理。类似的,在SDWebImage 5.0版本开始,也使用SDWebImageLoader进行加载,使用SDWebImageLoadersManager进行管理。网络下载使用SDWebImageDownloader进行。使用NSOperation进行下载管理。
[self downloadImageWithURL:url options:downloaderOptions context:context progress:progressBlock completed:completedBlock];
对比可知,LKImageKit在下载前做了很多优化,下载流程的管理更加细腻。SDWebImage的下载过程由于历史原因,虽然在5.0上大刀阔斧的改造了很多东西,但是整体流程会显得更加笨重一些。
3.图片解码
LKImageKit和SDWebImage的图片解码大同小异,以LKImageKit为例:
// 由LKImageDecoderManager进行解码器的管理
UIImage *image = [decoder imageFromData:data request:request error:&decode_error];
// 普通静图
result = [UIImage imageWithData:data scale:[UIScreen mainScreen].scale];
// 1帧的动图
UIImage *image = [UIImage imageWithCGImage:imageRef scale:[UIScreen mainScreen].scale orientation:orientation];
// 动图
UIImage *image = [UIImage animatedImageWithImages:images duration:INFINITY];
4.图片解压缩
图片解压缩是将解码后的image解出bitmap,从而避免系统主线程解压缩导致的主线程卡顿的问题。这里两个库也都是大同小异。以LKImageKit为例:
CGContextRef context = CGBitmapContextCreate(NULL, clipSize.width, clipSize.height, 8, 0, colorspace, bitmapInfo);
CGContextDrawImage(context, imageRect, input.CGImage);
CGImageRef cgimage = CGBitmapContextCreateImage(context);
UIImage *image = [UIImage imageWithCGImage:cgimage scale:screenScale orientation:input.imageOrientation];
不同的是,LKImageKit因为是用的指定的容器进行全链路的图片加载,所以可以通过打通全链路,在解压缩时,提前获取加载的容器大小,然后根据容器的大小进行按需解bitmap,从而节约内存。
开源SDWebImage并没有这个优化。我自己在SDWebImage上实现了一套类似的方案,需要从接口调用到解bitmap全链路打通,透传容器大小按需解码,以及对带alpha通道的webp图片等的异常case处理。略有不同的是,LKImageKit使用了image的scale属性,SDWebImage的scale默认1,需要做额外的pt->px的转换。后续有机会会把代码提到开源仓库。
0x2总结
两种图片方案有各自的业务场景和出发点,各有好处。
LKImageKit方案对图片的管理能力更强,图片加载的流程对于框架更加透明;SDWebImage方案更加灵活,尤其在5.0后,各种扩展性都非常好,LKImageKit的下载、解码管理有明显的借鉴SDWebImage的痕迹。另外,对于图片的加载流程,LKImageKit更加细腻,比如请求合并,按需解bitmap。
同时,从商业角度看,微信、QQ等大型App,是需要对图片加载全链路进行埋点监控的,这种强势掌控加载细节的LKImageKit方案,处理其这样的需求更加便利。猜想腾讯内部的LKImageKit版本应该还有埋点监控、网络接管等更多模块的适配接口,开源的只是删减了相关依赖的外部版本。