October 13, 2018
上篇说道 KingfisherManager.swift
下载图片,本篇打开 ImageDownloader.swift
文件看看到底 downloader 是怎么下载图片的
下载器的默认超时为 15 秒
/// The duration before the download is timeout. Default is 15 seconds.
open var downloadTimeout: TimeInterval = 15.0
每一个下载任务都有一对下载的进度和完成回调的元祖
/// A responder for authentication challenge.
/// Downloader will forward the received authentication challenge for the downloading session to this responder.
open weak var authenticationChallengeResponder: AuthenticationChallengeResponsable?
3 个分别为暂停、处理和取消的队列
barrierQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Barrier.\(name)", attributes: .concurrent)
processQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Process.\(name)", attributes: .concurrent)
cancelQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Cancel.\(name)")
downloadImage(with url: retrieveImageTask:options:progressBlock:completionHandler:) -> RetrieveImageDownloadTask?
方法主要做了几件事
- 如果下载任务 task 已经取消了就直接执行回调返回;
- 发送请求前可以最后修改网络请求;
- 调用 setup 初始化一个网络请求任务;
- 根据 URL 请求找到 task, 通知代理开始下载,保存当前下载器
fetchLoad
里的下载任务数 +1
而 setup(progressBlock:with completionHandler:for url: options:started: @escaping ((URLSession, ImageFetchLoad) -> Void))
初始化方法里主要做了
- 根据 url 获取一个 fetchLoad,判断
downloadTaskCount
是否为0, 如果是在取消队列中异步执行 内部函数prepareFetchLoad
方法,该方法内部用栅栏队列同步执行了started
闭包回调了 session 和 ImageFetchLoad 实例 - 不为 0,则直接调用
prepareFetchLoad()
回调
func prepareFetchLoad() {
barrierQueue.sync(flags: .barrier) {
let loadObjectForURL = fetchLoads[url] ?? ImageFetchLoad()
let callbackPair = (progressBlock: progressBlock, completionHandler: completionHandler)
loadObjectForURL.contents.append((callbackPair, options ?? KingfisherEmptyOptionsInfo))
fetchLoads[url] = loadObjectForURL
if let session = session {
started(session, loadObjectForURL)
}
}
}
if let fetchLoad = fetchLoad(for: url), fetchLoad.downloadTaskCount == 0 {
if fetchLoad.cancelSemaphore == nil {
fetchLoad.cancelSemaphore = DispatchSemaphore(value: 0)
}
cancelQueue.async {
_ = fetchLoad.cancelSemaphore?.wait(timeout: .distantFuture)
fetchLoad.cancelSemaphore = nil
prepareFetchLoad()
}
} else {
prepareFetchLoad()
}
ImageDownloaderSessionHandler 类继承自 NSObject,是 下载 session 的监听回调者,实现了 URLSessionDataDelegate
协议的几个重要方法,在各个合适方法里通知 ImageDownloader 的代理。
- 在
didReceive data
里,主线程回调 progressBlock,返回当前下载数据和总长度 - 在
didCompleteWithError error
方法里 会根据 url 去缓存里取图片,如果取到里则通知代理下载到了图片,否则执行completionHandler
闭包没有下载到图片