美文网首页
Alamofire Response源码解读

Alamofire Response源码解读

作者: 盾子 | 来源:发表于2019-08-27 20:36 被阅读0次

    前言

    Alamofire设计了2种与Request相对应的Response类型,他们分别是:

    • DefaultDataResponse / DataResponse -- >DataRequest,UploadRequest
    • DefaultDownloadResponse / DataResponse --> DownloadRequest
    • 如果使用了没有序列化的response方法,返回的就是带有Default开头的响应者,比如DefaultDataResponseDefaultDownloadResponse
    • 如果使用了序列化的response方法,返回的就是DataResponse或者DataResponse
    • DefaultDataResponse / DataResponse来举例,DataResponse基本上只比DefaultDataResponse多了一个系列化后的数据属性。

    Response流程分析

    先看一段简单代码

    SessionManager.default
        .request(urlString)
        .response { (response) in
            print(response)
        }
        .responseJSON { (jsonResponse) in
            print(jsonResponse)
    }
    
    • 因为Alamofire是采用的链式调用设计,所以可以在调用response后还能继续调用responseJSON。能实现链式访问的原理就是每个函数的返回值都是Self
    • 在上面的代码中,先调用了request,再调用了response。那么Alamofire是怎么保证response的的执行时机是在request发起请求并在数据回调之后再执行的呢?这里一定有猫腻,要不然只要调用了response方法就会里面执行里面的代码。进入到response方法里看看:
    public func response(queue: DispatchQueue? = nil, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Self {
        delegate.queue.addOperation {
            (queue ?? DispatchQueue.main).async {
                var dataResponse = DefaultDataResponse(
                    request: self.request,
                    response: self.response,
                    data: self.delegate.data,
                    error: self.delegate.error,
                    timeline: self.timeline
                )
    
                dataResponse.add(self.delegate.metrics)
    
                completionHandler(dataResponse)
            }
        }
    
        return self
    }
    
    • response加到了一个队列queue
    self.queue = {
        let operationQueue = OperationQueue()
    
        operationQueue.maxConcurrentOperationCount = 1
        operationQueue.isSuspended = true
        operationQueue.qualityOfService = .utility
    
        return operationQueue
    }()
    
    • operationQueue.maxConcurrentOperationCount = 1说明是一个串行队列
    • operationQueue.isSuspended = true队列默认是挂起状态
    • 相信看到这里就能够猜到具体是怎么实现的了。
      1. 调用response方法时把任务加到队列queue
      2. queue是一个串行队列,并且默认是挂起状态,所以先不执行任务
      3. 在请求完成的代理回调方法中把queue.isSuspended = false,开始执行队列中的任务。
    • 找到请求完成的代理回调发现确实如上所说。
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        if let taskDidCompleteWithError = taskDidCompleteWithError {
            taskDidCompleteWithError(session, task, error)
        } else {
            // 省略无关代码......
            queue.isSuspended = false
        }
    }
    

    DefaultDataResponse

    把目光移到response方法,创建了一个DefaultDataResponse对象并作为completionHandler的参数回调出去。

    public struct DefaultDataResponse {
        /// 表示该响应来源于那个请求
        public let request: URLRequest?
    
        /// 服务器返回的响应
        public let response: HTTPURLResponse?
    
        /// 响应数据
        public let data: Data?
    
        /// 在请求中可能发生的错误
        public let error: Error?
    
        /// 请求的时间线封装
        public let timeline: Timeline
        
        /// 包含了请求和响应的统计信息
        var _metrics: AnyObject?
    }
    
    • DefaultDataResponse是一个结构体类型,用来保存数据。一般来说,在Swift中,如果只是为了保存数据,那么应该把这个类设计成structstruct是值传递,因此对数据的操作更安全。
    • 把所有关于Response的数据全部保存在DefaultDataResponse结构体中,化零为整,很好的一个面向对象的设计原则,把这个完整的数据返回给用户,用户想用什么就取什么。
    • 我们来重点看下保存在DefaultDataResponse中的data是如何来的。继续来到response方法中:
    var dataResponse = DefaultDataResponse(
        request: self.request,
        response: self.response,
        data: self.delegate.data,
        error: self.delegate.error,
        timeline: self.timeline
    )
    
    • 点进去找到DataTaskDelegate里的data,发现其实返回的是mutableData,那么mutableData又是什么呢?
    override var data: Data? {
        if dataStream != nil {
            return nil
        } else {
            return mutableData
        }
    }
    
    • 在当前文件中搜索mutableData发现:
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
    
        if let dataTaskDidReceiveData = dataTaskDidReceiveData {
            dataTaskDidReceiveData(session, dataTask, data)
        } else {
            if let dataStream = dataStream {
                dataStream(data)
            } else {
                mutableData.append(data)
            }
    
            // 省略无关代码......
        }
    }
    
    • 在接收数据的代理方法中,把接收到的数据拼接在mutableData中。

    DataResponse<Value>

    DataResponse<Value>比上边的DefaultDataResponse多了一个public let result: Result<Value>属性,该属性存储了序列化后的数据。接着看看在Alamofire中是如何使用Result的,来到responseJSON方法中:

    public func responseJSON(
        queue: DispatchQueue? = nil,
        options: JSONSerialization.ReadingOptions = .allowFragments,
        completionHandler: @escaping (DataResponse<Any>) -> Void)
        -> Self
    {
        return response(
            queue: queue,
            responseSerializer: DataRequest.jsonResponseSerializer(options: options),
            completionHandler: completionHandler
        )
    }
    
    • 上边的这个函数的主要目的是把请求成功后的结果序列化为JSON再返回,completionHandler函数的参数类型为DataResponse<Any>,其中的Any就会传递给Result,也就是Result<Any>

    DataResponseSerializer

    一般来说,我们需要对response.data做序列化处理之后才方便使用。Alamofire已经提供了一些常用的序列化器,可以直接调用api使用。同样也可以自定义序列化器来实现自己功能。下面来看看responseJSON方法时怎么实现的。

    public func responseJSON(
        queue: DispatchQueue? = nil,
        options: JSONSerialization.ReadingOptions = .allowFragments,
        completionHandler: @escaping (DataResponse<Any>) -> Void)
        -> Self
    {
        return response(
            queue: queue,
            responseSerializer: DataRequest.jsonResponseSerializer(options: options),
            completionHandler: completionHandler
        )
    }
    
    • 其实内部也是调用的response方法,只是传了一个json序列化器作为参数
    public static func jsonResponseSerializer(
        options: JSONSerialization.ReadingOptions = .allowFragments)
        -> DataResponseSerializer<Any>
    {
        return DataResponseSerializer { _, response, data, error in
            return Request.serializeResponseJSON(options: options, response: response, data: data, error: error)
        }
    }
    
    • 初始化了一个DataResponseSerializer结构体,并且保存了一个尾随闭包serializeResponse
    • 通过前面对Response流程分析可以知道,当请求完成回调之后代码会执行到response方法中加入到队列的任务。也就会调用上面的尾随闭包。
    • 点击进入到Request.serializeResponseJSON方法
    public static func serializeResponseJSON(
        options: JSONSerialization.ReadingOptions,
        response: HTTPURLResponse?,
        data: Data?,
        error: Error?)
        -> Result<Any>
    {
        guard error == nil else { return .failure(error!) }
    
        if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) }
    
        guard let validData = data, validData.count > 0 else {
            return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength))
        }
    
        do {
            let json = try JSONSerialization.jsonObject(with: validData, options: options)
            return .success(json)
        } catch {
            return .failure(AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error)))
        }
    }
    
    • 返回的是一个Result<Any>类型的枚举,如果有错误信息,就返回return .failure(error!)并把错误信息做完枚举的关联值返回出去。
    • 如果成功就把结果序列化之后返回出去return .success(json)
    • 所以可以在处理请求结果时,可以直接通过dataResponse.result来判断请求结果成功还是失败。

    总结

    • response方法的响应结果回调completionHandler(dataResponse)加入到一个串联队列,并且这个队列是默认挂起的,当请求完成后队列开始执行,这样才能保证是在请求完成之后再回调结果。
    • 可以自定义系列化器,只需要自定义的序列化器实现DataResponseSerializerProtocol这个协议就可以。
    • DataResponse帮助我们统一管理请求过程中的数据,请求成功、失败、时间轴等等,便于业务层处理。

    有问题或者建议和意见,欢迎大家评论或者私信。
    喜欢的朋友可以点下关注和喜欢,后续会持续更新文章。

    相关文章

      网友评论

          本文标题:Alamofire Response源码解读

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