【AFNetworking 分析】图片下载

原创
2021/02/10 20:56
阅读数 147

Pre

AFImageDownloader 基础

简介

  • AFImageDownloader
    • 负责在已优先排序的队列上并行下载图像。
    • 传入的下载将根据下载优先级添加到队列的前面或后面。
    • 每个下载的映像都缓存在底层的NSURLCache和内存中。
    • 默认情况下,在图像缓存中具有缓存图像等价物的任何下载请求都将自动获得缓存图像表示。【对不起这句我没看懂,原文为:By default, any download request with a cached image equivalent in the image cache will automatically be served the cached image representation.】
  • AFImageDownloadReceipt
    • AFImageDownloader在启动数据任务时创建的下载凭证。
    • 它可用于取消在AFImageDownloader会话上运行的活动任务。
    • 一般来说,应该使用AFImageDownloadReceipt取消图像数据任务,而不是直接在task本身调用cancel
    • AFImageDownloader经过优化,可以处理重复的任务场景以及挂起与活动下载。

框架图

使用

AFImageDownloader 源码解析

初始化

defaultInstance

  • 调用init方法创建单例并返回

init

  1. 默认config
configuration.HTTPShouldSetCookies = YES;
configuration.HTTPShouldUsePipelining = NO;

configuration.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;
configuration.allowsCellularAccess = YES;
configuration.timeoutIntervalForRequest = 60.0;
configuration.URLCache = [AFImageDownloader defaultURLCache];
  1. 调用initWithSessionConfiguration:方法初始化

initWithSessionConfiguration:

  1. 用传入的config创建AFHTTPSessionManager对象,responseSerializer设置为AFImageResponseSerializer
  2. 调用initWithSessionManager:downloadPrioritization:maximumActiveDownloads:imageCache:方法初始化,入参:
    • sessionManager:步骤1创建的AFHTTPSessionManager对象
    • downloadPrioritizationAFImageDownloadPrioritizationFIFO
    • maximumActiveDownloads4
    • imageCache[[AFAutoPurgingImageCache alloc] init]

initWithSessionManager:downloadPrioritization:maximumActiveDownloads:imageCache:

  1. 根据入参设置对应4个属性及其它属性初始化
  2. 初始化synchronizationQueue为串行队列
  3. 初始化responseQueue为并发队列

下载

- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
                                                 withReceiptID:(NSUUID *)receiptID
                                                        success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse  * _Nullable response, UIImage *responseObject))success
                                                        failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;

于当前synchronizationQueue串行队列上同步执行

  1. 获取当前请求的URL的id,若为空,则在主队列上异步调用failure回调并返回
  2. 【先查询内存缓存】为已经存在的下载任务(复合操作)添加successfailure的回调。根据id在mergedTasks中获取AFImageDownloaderMergedTask对象,若不为空,为该对象添加处理回调(以uuid作为区分),并获取task【同一url多次调用下载的情况处理】
AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID success:success failure:failure];
[existingMergedTask addResponseHandler:handler];
task = existingMergedTask.task;
  1. 【再查询NSCache】如果缓存策略允许,尝试从图像缓存加载图像。在请求中有一个cachePolicy属性,若为NSURLRequestUseProtocolCachePolicy/NSURLRequestReturnCacheDataElseLoad/NSURLRequestReturnCacheDataDontLoad
  2. 若【内存缓存】和【沙盒中的Cache区域】中都找不到,则进行下载处理。创建NSURLSessionDataTaskresume。在其completionHandler中,异步+并发执行:下载完成后(不管是否出错)
    4.1 先用一个临时变量存储该任务
AFImageDownloaderMergedTask *mergedTask = [strongSelf safelyGetMergedTask:URLIdentifier];
if ([mergedTask.identifier isEqual:mergedTaskIdentifier]){
	···
}

  4.2 从self中安全移除该复合任务

mergedTask = [strongSelf safelyRemoveMergedTaskWithURLIdentifier:URLIdentifier];

  4.3 并调用对应的block进行处理
  - 若成功,将图片存储到内存缓存中,并调用success回调处理
  - 若错误,调用failure回调处理

  1. 存储响应处理程序,以便在请求完成时使用
AFImageDownloaderResponseHandler *handler =
	[[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID
							success:success
							failure:failure];

AFImageDownloaderMergedTask *mergedTask =
	[[AFImageDownloaderMergedTask alloc] initWithURLIdentifier:URLIdentifier
                                                  	 identifier:mergedTaskIdentifier
                                                  	task:createdTask];
[mergedTask addResponseHandler:handler];
self.mergedTasks[URLIdentifier] = mergedTask;
  1. 若活动请求数 < 最大限制,启动task;活动请求数 >= 最大限制,将复合任务添加到队列中(AFImageDownloaderqueuedMergedTasks数组)
  2. task不为空,则创建AFImageDownloadReceipt对象并返回
if (task) {
    return [[AFImageDownloadReceipt alloc] initWithReceiptID:receiptID task:task];
} else {
    return nil;
}

取消

- (void)cancelTaskForImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt;

同步函数+串行队列。dispatch_sync(self.synchronizationQueue, ^{}); 看源码可以直到,在所有的下载操作、取消操作、添加任务操作、获取任务操作、启动任务操作都是同步+串行执行的,保证线程安全。

  1. 获取传入receipt -> task -> originalRequest -> URL -> absoluteString得到identifier,根据identifier获取到指定复合任务
NSString *URLIdentifier = imageDownloadReceipt.task.originalRequest.URL.absoluteString;
AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier];

PS. 这里提一下,NSURLSessionTask中有originalRequestcurrentRequest两个NSURLRequest属性,currentRequest表示当前正在执行的任务,一般和originalRequest是一样的,除非发生重定向才会有所区别。而originalRequest则记录了重定向前的请求。

  1. 获取到符合条件的handlerindex
NSUInteger index = [mergedTask.responseHandlers indexOfObjectPassingTest:^BOOL(AFImageDownloaderResponseHandler * _Nonnull handler, __unused NSUInteger idx, __unused BOOL * _Nonnull stop) {
     return handler.uuid == imageDownloadReceipt.receiptID;
}];
  1. 若找到,拼接错误信息并调用该handlerfailureBlock
  2. 若该复合任务没有handler了,取消task并移除本复合任务

内存缓存 & NSCache

  • 内存缓存其实就是本次程序启动创建的内存空间(此处mergedTasks即存在内存缓存中),其实就是临时的一份变量。
  • NSCache
    • 地址:/Users/你的用户名/Library/Developer/设备(e.g.CoreSimulator)/Devices/xxxxxx(应用程序id)/Library/Caches/
    • 该沙盒区域随着app的存在一直存在于设备中
展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部