美文网首页
KingFisher源码解析 - 网络请求

KingFisher源码解析 - 网络请求

作者: milawoai | 来源:发表于2019-07-25 19:28 被阅读0次

KingFisher使用URLSession来进行网络请求操作。刨去一切无关元素,让我们来看一下最简单的URLSession:

var session: URLSession? = URLSession(
    configuration: URLSessionConfiguration.ephemeral,
    delegate: URLSessionDelegate.class,
    delegateQueue: nil)
let session1 = session!.dataTask(with: URL(string: "https://api.xxxxx.com/hello")!) 
session1.resume()

网络请求就是定义一个使用协议和configuration定义一个URLSession,每个网络请求都是这个session通过dataTask函数生成的task,task有run(运行),cancel(取消),suspend(挂起)等状态。其初始化后,task处于挂起状态,通过resume激活。

在上一篇中,我们知道KingFisher的下载图片使用了ImageDownloader的downloadImage方法:

class ImageDownloader {
func downloadImage(
        with url: URL,
        options: KingfisherParsedOptionsInfo,
        completionHandler: ((Result<ImageLoadingResult, KingfisherError>) -> Void)? = nil) -> DownloadTask? {
}

其本质上就是对URLSession的一套封装.

1. 主要流程说明

1. 首先 创立一个不使用缓存的URLRequest


var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: downloadTimeout)

2. 其次 将 completionHandler block 转换成 TaskCallback


   let onCompleted = completionHandler.map {
            block -> Delegate<Result<ImageLoadingResult, KingfisherError>, Void> in
            let delegate =  Delegate<Result<ImageLoadingResult, KingfisherError>, Void>()
            delegate.delegate(on: self) { (_, callback) in
                block(callback)
            }
            return delegate
}

let callback = SessionDataTask.TaskCallback(
            onCompleted: onCompleted,
            options: options
        )

3. 生成DownloadTask

DownloadTask是一个结构体, 包括

public struct DownloadTask {
    public let sessionTask: SessionDataTask
    public let cancelToken: SessionDataTask.CancelToken
}

ImageDownloader通过一个sessionDelegate维持一个 SessionDataTask的队列。
通过url查询是否存在相同url的SessionDataTask,如存在,则根据这个SessionDataTask生成DownloadTask。
否则使用session.dataTask生成一个URLSessionDataTask,将其转换成SessionDataTask,并将其加入到task队列中

4. 配置回调

SessionDataTask的回调是通过Delegate onTaskDone中进行处理的。
onTaskDone的定义如下:

let onTaskDone = Delegate<(Result<(Data, URLResponse?), KingfisherError>, [TaskCallback]), Void>()

它的配置如下:

sessionTask.onTaskDone.delegate(on: self) { (self, done) in
let (result, callbacks) = done
let value = try result.get()
switch result {
   case .success(let (data, response)):
    let imageResult = result.map { ImageLoadingResult(image: $0, url: url, originalData: data) }
    let queue = callback.options.callbackQueue
    queue.execute { callback.onCompleted?.call(imageResult) }
}

5. 启动

sessionTask.resume()

接下来,我们会对请求中涉及的数据结构来详细解释

2. Delegate 与 Result

在代码中,我们涉及了两类特殊的数据:DelegateResult

block -> Delegate<Result<ImageLoadingResult, KingfisherError>, Void>

这两个数据在KingFisher中非常通用,需要进行解释一下

2.1 Delegate

Delegate可以理解成一个block的包裹器,但是可以避免使用 self,从而防止循环引用。

class Delegate<Input, Output> {
    init() {}
    
    private var block: ((Input) -> Output?)?
    
    func delegate<T: AnyObject>(on target: T, block: ((T, Input) -> Output)?) {
    self.block = { [weak target] input in
       guard let target = target else { return nil }
       return block?(target, input)
     }
 }
    
    func call(_ input: Input) -> Output? {
        return block?(input)
    }
}

他的使用方法很简单:

  1. 通过func delegate设置回调
let delegate =  Delegate<Result<ImageLoadingResult, KingfisherError>, Void>()
delegate.delegate(on: self) { (_, callback) in
    block(callback)
}

delegate接受Input:Result<ImageLoadingResult, KingfisherError>, 输出Void

  1. 适应func call进行调用
delegate.call(imageResult)
->

2.2 Result

ResultSwift5引入的一种枚举,接受SuccessFailure两种数据,其中Failure必须继承Error。用户在使用的时候可以通过
success或者failure两种结构获取数据。特别适合在网络请求等可能出现错误的场合使用,用户只需要提供一种回调即可。

public enum Result<Success, Failure> where Failure : Error {

    /// A success, storing a `Success` value.
    case success(Success)

    /// A failure, storing a `Failure` value.
    case failure(Failure)
}

其定义如下:

let result: Result<URLResponse, KingfisherError>
if let error = error {
   result = .failure(KingfisherError.responseError(reason: .URLSessionError(error: error)))
} else if let response = task.response {
   result = .success(response)
} else {
   result = .failure(KingfisherError.responseError(reason: .noURLResponse(task: sessionTask)))
}

其使用有两种办法:

  1. 使用get,如果失败会直接throw error
 do {
    let value = try result.get()
  
} catch {

}
  1. 使用Switch
 switch result {
    //URLResponse
    case .success(let response):
    case .failure(let error):
 }

3. 网络请求task的封装管理

在第一小节的生成DownloadTask中,我们提到了好几种Task

DownloadTaskSessionDataTaskURLSessionDataTask

其中URLSessionDataTask是网络请求的抽象task,这里暂时不讨论。

此外,我们提到这些task被存在一个队列中。这个管理队列是由

private let sessionDelegate: SessionDelegate 进行管理的

3.1 SessionDataTask

SessionDataTaskURLSessionDataTask的封装,可以为URLSessionDataTask添加一系列回调,并对这些回调进行管理。至于为何会一个SessionDataTask对应多个回调,我们会在下面介绍DownloadTask的时候进行学习

public class SessionDataTask {


//*******网络请求Task的管理************
    public let task: URLSessionDataTask // 封装的网络请求task
    public private(set) var mutableData: Data // 封装的task请求中下载的原始数据
    var started = false // task 是否进入请求状态
    
//*******网络请求Task回调CallBack的管理************ 
    struct TaskCallback {
        let onCompleted: Delegate<Result<ImageLoadingResult, KingfisherError>, Void>? // CallBack的本体
        let options: KingfisherParsedOptionsInfo // 一些配置数据
    }// 回调CallBack的封装
    public typealias CancelToken = Int // CancelToken是取消回调的凭证
    
    private var callbacksStore = [CancelToken: TaskCallback]() // CallBack将存储于此,方便取消
    
    var callbacks: [SessionDataTask.TaskCallback] // 将callbacksStore数据转成Array
    
    func addCallback(_ callback: TaskCallback) -> CancelToken //添加回调
    func removeCallback(_ token: CancelToken) -> TaskCallback? //移除回调
    
    func cancel(token: CancelToken) {
         let callback = removeCallback(token)
         onCallbackCancelled.call((token, callback))
    } //移除回调并启用cancel回调,如果callbacksStore没有数据了,直接cancelTask
    
    //*******SessionDataTask自身的两个Delegate,回调TaskCallback通过这两个Delegate通知给外界************ 
    let onTaskDone = Delegate<(Result<(Data, URLResponse?), KingfisherError>, [TaskCallback]), Void>()
    //Input: (Result<(Data, URLResponse?), KingfisherError>, [TaskCallback]) 这样一个元组
    let onCallbackCancelled = Delegate<(CancelToken, TaskCallback), Void>()
    //Input: (CancelToken, TaskCallback) 这样一个元组,onCallbackCancelled 回调是给SessionDelegate使用的
}

onTaskDone在第一节第四小节的配置回调中配置。
onCallbackCancelled在下面要将的SessionDelegate中配置。

3.2 DownloadTask

DownloadTask是一个结构体,是SessionDataTask的封装,也是downImage函数的返回值。
多个DownloadTask可以关联到同一个SessionDataTask上。还记得SessionDataTask可以有多个callback吗,就是为了这个场景准备的.

其结构如下:

public struct DownloadTask {
    public let sessionTask: SessionDataTask //关联到此`DownloadTask`的`SessionDataTask`
    public let cancelToken: SessionDataTask.CancelToken //DownloadTask的token,用于取消自己
    public func cancel() {
        sessionTask.cancel(token: cancelToken)
    }
}

3.3 SessionDelegate

SessionDelegate提供了对task的管理队列,这个task是3.1中的SessionTask. 此外,SessionDelegate实现了URLSessionDataDelegate, 提供了一系列Delegate对网络请求的状态进行管理.


private var tasks: [URL: SessionDataTask] = [:] //URL:SessionDataTask的字典

func add(
        _ dataTask: URLSessionDataTask,
        url: URL,
        callback: SessionDataTask.TaskCallback) -> DownloadTask {// 添加url:task 入tasks 
        
  let task = SessionDataTask(task: dataTask) // 生成 SessionDataTask
  task.onCallbackCancelled.delegate(on: self) // 设置task的取消回调
      { [unowned task] (self, value) in
        let (token, callback) = value
        let error = KingfisherError.requestError(reason: .taskCancelled(task: task, token: token))
        task.onTaskDone.call((.failure(error), [callback])) // task的取消回调对外界不会暴露,调用onTaskDone
        if !task.containsCallbacks {
            let dataTask = task.task
            self.remove(dataTask) //要是SessionDataTask对应的DownloadTask全部被取消,根据SessionDataTask对应的URLSessionTask的url,将SessionDataTask从tasks中取消掉
          }
   }
        
    let token = task.addCallback(callback) //为SessionDataTask绑定callback
    tasks[url] = task
    return DownloadTask(sessionTask: task, cancelToken: token) //为SessionDataTask绑定DownloadTask  
 } 
 
 func task(for task: URLSessionTask) -> SessionDataTask? //根据URLSessionTask查出SessionDataTask
 func task(for url: URL) -> SessionDataTask? //根据 URL查出SessionDataTask
 
//##### 一系列Delegate, 供给URLSessionDataDelegate

let onValidStatusCode = Delegate<Int, Bool>()
let onDownloadingFinished = Delegate<(URL, Result<URLResponse, KingfisherError>), Void>()
let onDidDownloadData = Delegate<SessionDataTask, Data?>()

let onReceiveSessionChallenge = Delegate<SessionChallengeFunc, Void>()
let onReceiveSessionTaskChallenge = Delegate<SessionTaskChallengeFunc, Void>()

SessionDelegate实现了URLSessionDataDelegate,对URLSessionTask的返回进行包装和处理。以

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)

举例,这个函数是URLSessionTask请求完成的回调。

首先,我们从tasks中找到对应的SessionTask

guard let sessionTask = self.task(for: task) else { return }

其次,这个时候下载已经完成,我们需要处理 Delegate onDownloadingFinished

let onDownloadingFinished = Delegate<(URL, Result<URLResponse, KingfisherError>), Void>()

新建一个Result<URLResponse, KingfisherError>,根据error是否为空,为其赋予.success()与.failure()

其次,这个时候下载的data已经获取到,我们需要处理 Delegate onDidDownloadData,我们可以这个Delegate中对Data进行统一处理。

let onDidDownloadData = Delegate<SessionDataTask, Data?>()

let data = onDidDownloadData.call(sessionTask)

最后,如果Data存在且error不为空,移除URLSessionTask,调用SesssionTask的onTaskDone回调,通知外界数据已经成功获取。

sessionTask.onTaskDone.call((result, sessionTask.callbacks))

相关文章

网友评论

      本文标题:KingFisher源码解析 - 网络请求

      本文链接:https://www.haomeiwen.com/subject/ssekrctx.html