0x0背景
原本是放到自己博客的,不怎么用了,把文章同步過來,原文地址[iOS/OC]SDWebImage和LKImage對比
LKImageKit是騰訊開源的一個高性能圖片加載框架,雖然第一時間下載了源碼,但是只是簡單的看了框架,沒有細致的研讀源碼。最近空閑下來,學習了一下LKImageKit源碼,其中有很多巧妙的實現(xiàn)。本文將通過1張圖片的加載流程,對比兩個圖片框架。
0x1正文
LKImageKit和SDWebImage分別選用了兩種不同的圖片加載方案。LKImageKit的圖片加載是提供了一個LKImageView,加載圖片需要使用指定的容器。SDWebImage是寫了一個Category,加載圖片可以使用系統(tǒng)的UIImageView或其子類。流程上。
1.入口
發(fā)起一個圖片加載,LKImageKit是在layoutSubviews時,發(fā)起1次圖片加載,SDWebImage提供了1個主動發(fā)起圖片加載請求的接口sd_setImageWithUrl
LKImageKit發(fā)起圖片加載:
// 上層調(diào)用
imageView.URL = [NSURL urlWithString:@"https://xxx.png"];
imageView.request.synchronized = YES;
// 底層加載
- (void)layoutSubviews {
[super layoutSubviews];
[self layoutAndLoad];
}
SDWebImage發(fā)起圖片加載
// 上層調(diào)用
[imageView sd_setImageWithURL:[NSURL URLWithString:@""]];
// 之后開始下載流程
兩個圖片庫都提供了加載回調(diào),不同的是,LKImageKit提供的是delegate的方法,SDWebImage提供的是callback
LKImageKit
@protocol LKImageViewDelegate <NSObject>
@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進行圖片下載會統(tǒng)一由LKImageManger進行管理,LKImageKit提供的請求合并的優(yōu)化就是通過LKImageManager進行的。請求會通過blockOperation放到1個operationQueue中進行多個加載請求的隊列管理。
資源加載則由LKImageLoader進行,由單例LKImageLoaderManager進行管理。網(wǎng)絡下載是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進行管理。網(wǎng)絡下載使用SDWebImageDownloader進行。使用NSOperation進行下載管理。
[self downloadImageWithURL:url options:downloaderOptions context:context progress:progressBlock completed:completedBlock];
對比可知,LKImageKit在下載前做了很多優(yōu)化,下載流程的管理更加細膩。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,從而避免系統(tǒng)主線程解壓縮導致的主線程卡頓的問題。這里兩個庫也都是大同小異。以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因為是用的指定的容器進行全鏈路的圖片加載,所以可以通過打通全鏈路,在解壓縮時,提前獲取加載的容器大小,然后根據(jù)容器的大小進行按需解bitmap,從而節(jié)約內(nèi)存。
開源SDWebImage并沒有這個優(yōu)化。我自己在SDWebImage上實現(xiàn)了一套類似的方案,需要從接口調(diào)用到解bitmap全鏈路打通,透傳容器大小按需解碼,以及對帶alpha通道的webp圖片等的異常case處理。略有不同的是,LKImageKit使用了image的scale屬性,SDWebImage的scale默認1,需要做額外的pt->px的轉(zhuǎn)換。后續(xù)有機會會把代碼提到開源倉庫。
0x2總結(jié)
兩種圖片方案有各自的業(yè)務場景和出發(fā)點,各有好處。
LKImageKit方案對圖片的管理能力更強,圖片加載的流程對于框架更加透明;SDWebImage方案更加靈活,尤其在5.0后,各種擴展性都非常好,LKImageKit的下載、解碼管理有明顯的借鑒SDWebImage的痕跡。另外,對于圖片的加載流程,LKImageKit更加細膩,比如請求合并,按需解bitmap。
同時,從商業(yè)角度看,微信、QQ等大型App,是需要對圖片加載全鏈路進行埋點監(jiān)控的,這種強勢掌控加載細節(jié)的LKImageKit方案,處理其這樣的需求更加便利。猜想騰訊內(nèi)部的LKImageKit版本應該還有埋點監(jiān)控、網(wǎng)絡接管等更多模塊的適配接口,開源的只是刪減了相關依賴的外部版本。