Kingfisher 源码解析(三)

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? 方法主要做了几件事

  1. 如果下载任务 task 已经取消了就直接执行回调返回;
  2. 发送请求前可以最后修改网络请求;
  3. 调用 setup 初始化一个网络请求任务;
  4. 根据 URL 请求找到 task, 通知代理开始下载,保存当前下载器
  5. 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 闭包没有下载到图片

comments powered by Disqus