Kingfisher源码之ImageDownLoader

2020-08-25  本文已影响0人  Mannyao

ImageDownloader请求图片url下载管理
先看初始化方法:

   public init(name: String) {
        if name.isEmpty {
            fatalError("must have a name")
        }
        self.name = name
        sessionDelegate = SessionDelegate()
        session = URLSession(configuration: sessionConfiguration, delegate: sessionDelegate, delegateQueue: nil)
        authenticationChallengeResponder = self
        setupSessionHandler()
    }

1.初始化一个SessionDelegate
2.初始化一个URLSession
ImageDownloader中有自己的delegate,ImageDownloaderDelegte提供下载的回调

public protocol ImageDownloaderDelegte: AnyObject {
    func imageDownloader(_ downloader: ImageDownloader, willDownloadImageForURL url: URL, with request: URLRequest?)
    func imageDownloader(_ downloader: ImageDownloader, didFinishDownloadingImageForURL url: URL, with response: URLResponse?, error: Error?)
    func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data?
    func imageDownloader(_ downloader: ImageDownloader, didDownload image: Image, for url: URL, with response: URLResponse?)
    
    func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool
}

重点看图片下载的过程

下载步骤解析

1.创建URLRequest
2.对request的修改
3.将参数中的progressBlock和completionHandler分别转为TaskCallback中的onprogress和onCompleted
4.创建TaskCallback
5.创建sessionDataTask
6.转接sessionTask完成的回调到ImageDownloaderDelegte
7.启动sessionTask

  @discardableResult
    func downloadImage(with url: URL,
                       options: KingfisherParedOptionsInfo,
                       progressBlock: DownloadProgressBlock? = nil,
                       completionHandler: ((Result<ImageLoadingResult, KingfisherError>) -> Void)? = nil) -> DownloadTask? {
        //1.创建URLRequest
        var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: downloadTimeout)
        request.httpShouldUsePipelining = requestUserPipelining
        //2.对request的修改
        if let requestModifier = options.requestModifier {
            guard let r = requestModifier.modified(for: request) else {
                options.callbackQueue.exectue {
                    completionHandler?(.failure(KingfisherError.requestError(reason: .emptyRequest)))
                }
                return nil
            }
            request = r
        }
        
        guard let url = request.url, !url.absoluteString.isEmpty else {
            options.callbackQueue.exectue {
                completionHandler?(.failure(KingfisherError.requestError(reason: .emptyRequest)))
            }
            return nil
        }
        //3.将参数中的progressBlock和completionHandler分别转为TaskCallback中的onprogress和onCompleted
        let onProgress = progressBlock.map {
            block -> Delegate<(Int64, Int64), Void> in
            let delegate = Delegate<(Int64, Int64), Void>()
            delegate.delegate(on: self) { (_, progress) in
                let (downloaded, total) = progress
                block(downloaded, total)
            }
            return delegate
        }
        let onCompleted = completionHandler.map {
            block -> Delegate<Result<ImageLoadingResult, KingfisherError>, Void> in
            let delegate = Delegate<Result<ImageLoadingResult, KingfisherError>, Void>()
            delegate.delegate(on: self) { (_, result) in
                block(result)
            }
            return delegate
        }
        //4.创建TaskCallback
        let callback = SessionDataTask.TaskCallback(onprogress: onProgress, onCompleted: onCompleted, options: options)
        let downloadTask: DownloadTask
        //5.创建sessionDataTask
        if let existingTask = sessionDelegate.task(for: url) {
            downloadTask = sessionDelegate.append(existingTask, url: url, callback: callback)
        } else {
            let sessionDataTask = session.dataTask(with: request)
            sessionDataTask.priority = options.downloadPriority
            downloadTask = sessionDelegate.add(sessionDataTask, url: url, callback: callback)
        }
        let sessionTask = downloadTask.sessionTask
        if !sessionTask.started {
            //6.转接sessionTask完成的回调到ImageDownloaderDelegte
            sessionTask.onTaskDone.delegate(on: self) { (self, done) in
                let (result, callbacks) = done
                do {
                    let value = try result.get()
                    self.delegate?.imageDownloader(self, didFinishDownloadingImageForURL: url, with: value.1, error: nil)
                } catch {
                    self.delegate?.imageDownloader(self, didFinishDownloadingImageForURL: url, with: nil, error: error)
                }
                switch result {
                case .success(let (data, response)):
                    let processor = ImageDataProcessor(data: data, callbacks: callbacks, processingQueue: options.processingQueue)
                    processor.onImageProcessed.delegate(on: self) { (self, result) in
                        let (result, callback) = result
                        if let image = try? result.get() {
                           self.delegate?.imageDownloader(self, didDownload: image, for: url, with: response)
                        }
                        let imageResult = result.map { ImageLoadingResult(image: $0, url: url, originalData: data) }
                        let queue = callback.options.callbackQueue
                        queue.exectue {
                            callback.onCompleted?.call(imageResult)
                        }
                    }
                    processor.process()
                case .failure(let error):
                    callbacks.forEach { callback in
                        let queue = callback.options.callbackQueue
                        queue.exectue {
                            callback.onCompleted?.call(.failure(error))
                        }
                    }
                }
            }
            //7.启动sessionTask
            sessionTask.resume()
        }
        return downloadTask
    }

下载方法有两个闭包,一个返回进度,一个返回结果

取消Task

通过自己的属性sessionDelegate类取消

//MARK: Cancelling Task
extension ImageDownloader {
    public func cancelAll() {
        sessionDelegate.cancelAll()
    }
    
    public func cancel(url: URL) {
        sessionDelegate.cancel(url: url)
    }
}
上一篇下一篇

猜你喜欢

热点阅读