SDWebImage源代码解析(二)Downloader

本文深入剖析了SDWebImage库中的下载模块实现原理,包括SDWebImageDownloader和SDWebImageDownloaderOperation两个核心组件的设计思路及关键技术点。文章详细介绍了图片下载流程、并发控制、图片渐进式加载等特性。

前言

本文简书地址:http://www.jianshu.com/p/685e9b7ec4b2
本文的中文注释代码demo更新在我的github上。

上篇文章讲解的了SDWebImage的Cache部分,这篇讲讲一下Download部分。

Download

Download部分主要包含如下2个方法
* SDWebImageDownloader
图片下载控制类
* SDWebImageDownloaderOperation
NSOperation子类

SDWebImageDownloader

SDWebImageDownloader主要包含以下内容:
* 初始化信息
* 相关请求信息设置与获取
* 请求图片方法
* NSURLSession相关回调

初始化信息

SDWebImageDownloader.h中的property声明

//下载完成执行顺序
typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
    //先进先出
    SDWebImageDownloaderFIFOExecutionOrder,
    //先进后出
    SDWebImageDownloaderLIFOExecutionOrder
};

@interface SDWebImageDownloader : NSObject
//是否压缩图片
@property (assign, nonatomic) BOOL shouldDecompressImages;
//最大下载数量
@property (assign, nonatomic) NSInteger maxConcurrentDownloads;
//当前下载数量
@property (readonly, nonatomic) NSUInteger currentDownloadCount;
//下载超时时间
@property (assign, nonatomic) NSTimeInterval downloadTimeout;
//下载策略
@property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder;

SDWebImageDownloader.m中的Extension

@interface SDWebImageDownloader () <NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
//下载的queue
@property (strong, nonatomic) NSOperationQueue *downloadQueue;
//上一个添加的操作
@property (weak, nonatomic) NSOperation *lastAddedOperation;
//操作类
@property (assign, nonatomic) Class operationClass;
//url请求缓存
@property (strong, nonatomic) NSMutableDictionary *URLCallbacks;
//请求头
@property (strong, nonatomic) NSMutableDictionary *HTTPHeaders;
// This queue is used to serialize the handling of the network responses of all the download operation in a single queue
//这个queue为了能否序列化处理所有网络结果的返回
@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t barrierQueue;

// The session in which data tasks will run
//数据runsession
@property (strong, nonatomic) NSURLSession *session;

@end

相关方法实现如下:

//判断用了SDNetworkActivityIndicator,去替换该方法的内容
+ (void)initialize {
    // Bind SDNetworkActivityIndicator if available (download it here: http://github.com/rs/SDNetworkActivityIndicator )
    // To use it, just add #import "SDNetworkActivityIndicator.h" in addition to the SDWebImage import
    if (NSClassFromString(@"SDNetworkActivityIndicator")) {

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")];
#pragma clang diagnostic pop

        // Remove observer in case it was previously added.
        [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStartNotification object:nil];
        [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStopNotification object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:activityIndicator
                                                 selector:NSSelectorFromString(@"startActivity")
                                                     name:SDWebImageDownloadStartNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:activityIndicator
                                                 selector:NSSelectorFromString(@"stopActivity")
                                                     name:SDWebImageDownloadStopNotification object:nil];
    }
}

//单例
+ (SDWebImageDownloader *)sharedDownloader {
    static dispatch_once_t once;
    static id instance;
    dispatch_once(&once, ^{
        instance = [self new];
    });
    return instance;
}

//初始化方法
- (id)init {
    if ((self = [super init])) {
        _operationClass = [SDWebImageDownloaderOperation class];
        _shouldDecompressImages = YES;
        _executionOrder = SDWebImageDownloaderFIFOExecutionOrder;
        _downloadQueue = [NSOperationQueue new];
        _downloadQueue.maxConcurrentOperationCount = 6;
        _downloadQueue.name = @"com.hackemist.SDWebImageDownloader";
        _URLCallbacks = [NSMutableDictionary new];
#ifdef SD_WEBP
        _HTTPHeaders = [@{@"Accept": @"image/webp,image/*;q=0.8"} mutableCopy];
#else
        _HTTPHeaders = [@{@"Accept": @"image/*;q=0.8"} mutableCopy];
#endif
        _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
        _downloadTimeout = 15.0;

        NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
        sessionConfig.timeoutIntervalForRequest = _downloadTimeout;

        /**
         *  Create the session for this task
         *  We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate
         *  method calls and completion handler calls.
         */
        //session初始化
        self.session = [NSURLSession sessionWithConfiguration:sessionConfig
                                                     delegate:self
                                                delegateQueue:nil];
    }
    return self;
}

- (void)dealloc {
    //停止session
    [self.session invalidateAndCancel];
    self.session = nil;

    //停止所有downloadqueue
    [self.downloadQueue cancelAllOperations];
    SDDispatchQueueRelease(_barrierQueue);
}

从这边也可以看出,SDWebImageDownloader的下载方法就是用NSOperation。使用NSOperationQueue去控制最大操作数量和取消所有操作,在NSOperation中运行NSUrlSession方法请求参数。

相关请求信息设置与获取

相关方法如下:

//设置hedaer头
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field {
    if (value) {
        self.HTTPHeaders[field] = value;
    }
    else {
        [self.HTTPHeaders removeObjectForKey:field];
    }
}

- (NSString *)valueForHTTPHeaderField:(NSString *)field {
    return self.HTTPHeaders[field];
}

//设置多大并发数量
- (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads {
    _downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads;
}

- (NSUInteger)currentDownloadCount {
    return _downloadQueue.operationCount;
}

- (NSInteger)maxConcurrentDownloads {
    return _downloadQueue.maxConcurrentOperationCount;
}

//设置operation的类型,可以自行再继承子类去实现
- (void)setOperationClass:(Class)operationClass {
    _operationClass = operationClass ?: [SDWebImageDownloaderOperation class];
}

//暂停下载
- (void)setSuspended:(BOOL)suspended {
    [self.downloadQueue setSuspended:suspended];
}
//取消所有下载
- (void)cancelAllDownloads {
    [self.downloadQueue cancelAllOperations];
}

这一部分唯一提一下的是setOperationClass:方法,是为了可以自己实现NSOperation的子类去完成相关请求的设置。

请求图片方法

相关方法实现如下:

//请求图片方法
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock {
    __block SDWebImageDownloaderOperation *operation;
    __weak __typeof(self)wself = self;

    //处理新请求的封装
    [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^{
        NSTimeInterval timeoutInterval = wself.downloadTimeout;
        if (timeoutInterval == 0.0) {
            timeoutInterval = 15.0;
        }

        // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
        //为了防止潜在的重复cache
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];
        //设置是否处理cookies
        request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
        request.HTTPShouldUsePipelining = YES;
        //有header处理block,则调用block,返回headerfieleds
        if (wself.headersFilter) {
            request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]);
        }
        else {
            request.allHTTPHeaderFields = wself.HTTPHeaders;
        }
        //这边的创建方式允许自己定义SDWebImageDownloaderOperation的子类进行替换
        operation = [[wself.operationClass alloc] initWithRequest:request
                                                        inSession:self.session
                       
浏览并下载网页上的图片。 Image Downloader不出售,并且将始终是免费,开源的,并且没有广告或任何形式的跟踪算法! 您可以在此处找到源代码:https://github.com/vdsabev/image-downloader Image Downloader =================如果您需要从网页批量下载图像,使用此扩展程序,您可以:-查看页面包含的图像以及链接到的图像-按宽度,高度和URL进行过滤; 支持通配符和正则表达式-(可选)仅显示链接中的图像-通过单击图像选择要下载的图像-使用专用按钮下载或在新标签页中打开单个图像-自定义图像的显示宽度,列,边框大小和颜色-隐藏过滤器,不需要的按钮和通知按下“下载”按钮时,所有选定的图像都将保存到Chrome的默认下载目录,或者如果指定子文件夹名称,则保存到其中的目录。 警告:如果尚未设置默认的下载目录,则必须手动选择每个图像的保存位置,这可能会打开许多​​弹出窗口。 不建议您尝试一次在没有默认下载目录的情况下一次下载太多图像。 已知问题===============此扩展程序无法始终提取单击照片时打开的全分辨率图像(例如,在Facebook相册中)。 那是因为页面没有直接链接到图像,而是使用了脚本。 如果您需要那种功能,那么即使您无法大量下载图像,也可以使用诸如Hover Zoom的其他扩展。 更改日志=============== 2.4.2:-Chrome不允许访问跨域CSS规则的解决方法2.4.1:-修复了无效URL会破坏扩展名的问题- https://github.com/vdsabev/image-downloader/issues/23-将Zepto.js更新为1.2.0 2.4:-添加了在下载2.3前重命名文件的选项:-添加了对BMP,SVG和WebP图像的支持-增加了对相对URL的支持-通过搜索更少的元素提高了弹出窗口的加载速度-用chrome.runtime 2.2代替了不推荐使用的chrome.extension调用:-删除了访问标签的不必要权限-删除了由于提示而导致的捐赠提示一些用户认为它在第一次出现后并没有消失; 现在,将在首次安装时打开选项页-保存URL过滤器的值-修复某些尺寸调整问题的另一种尝试2.1:-添加了图像宽度/高度过滤器-由于某些原因,一次性重置了所有设置遇到尺寸问题的人-删除了按URL排序选项2.0:-添加了将文件保存到子文件夹的功能-利用Google Chrome下载API-实施了基于网格的更简洁设计-单击图像URL文本框现在将自动选择文本以便用户可以复制文本-修复了一些小的显示问题-添加了列数设置,删除了边框样式设置-选项页1.3中添加了捐赠按钮:-现在,样式标签中使用的图像也将包含在列表的末尾。 仅包含来自元素的内联样式属性的图像。 -新增了对数据URI的支持-若干错误修复和优化1.2:-更改了要显示在只读文本框中的图像上方的URL-将图像复选框移至顶部,并在每个图像复选框下方添加了打开和下载按钮-最初禁用了“下载”按钮和“所有”复选框-引入了一些新选项来隐藏过滤器,按钮和通知-删除了正文宽度选项; 弹出窗口的宽度现在相对于最大图像宽度选项重新调整大小-简化了设计1.1:-修复了最小和最大图像宽度的保存-在图像本身上方添加了URL以及用于切换的选项-添加了通配符过滤器模式(沿正常和正则表达式)-现在将保存所选过滤器的状态-将“按URL排序”选项移回过滤器-在选项页面中添加了“清除数据”按钮。 尽管该扩展程序尚未使用大量本地存储,但有人可能会喜欢此选项。 -重构了大量代码,尤其是本地存储1.0.13的使用:-添加了一条通知,使用户知道下载已开始-添加了一些动画并进一步完善了选项通知-修复了一些正在处理的事件处理程序多次附加1.0.12:-迁移到jQuery-为“所有”复选框实现了不确定的状态-如果未选中任何图像,则将禁用“下载”按钮-修复了带有重置选项的错误-现在用户可以选择保存重置值或只是通过重新加载页面来取消重置-就像通知1.0.11中所述:-更改了下载机制以支持Chrome v21 +-添加了“仅显示链接的图像”过滤器选项,当您只想下载页面上URL中的图像。 1.0.10:-添加了下载确认1.0.9:-图片数量现在将显示在“所有”复选框1.0.8旁:-添加了对锚定标记中的图片URL的检测; 请注意,此功能将不会检测不具有.jpg,.jpeg,.gif或.png文件扩展名的URL-它依赖于正则表达式,以避免可能向外部服务器发送1.0.7的数百个请求:-已删除当您按下“下载”时弹出的桌面通知系统,显示的文字描述应该易于控制(通过“选项”)并且不易受到干扰; 这也需要较少的扩展权限-添加了隐藏下载通知的选项; 大
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值