Alamofire-Request知识补充

2019-08-23  本文已影响0人  king_jensen

一、请求适配器-RequestAdapter

目的是为了处理Request添加装饰,一个典型的例子是为每一个请求添加token请求,或者对Request重定向。

public protocol RequestAdapter {
    func adapt(_ urlRequest: URLRequest) throws -> URLRequest
}

RequestAdapter是一个协议,我们需要自己来实现这个协议。

class JNAdapter: RequestAdapter{
    func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
        // token
        //  request 处理
        var request = urlRequest
        request.setValue("xxxxxx", forHTTPHeaderField: "token")

 //request 重定向
        let newUrlRequest = URLRequest.init(url: URL(string: "http://www.douban.com/j/app/radio/channels")!)
        return newUrlRequest
    }
}

1.定义JNAdapter遵循RequestAdapter协议
2.实现adapt,实现自己需要处理的逻辑
3.SessionManager.default.adapter = JNAdapter(),指定adapter

二、自定义验证-validate

网络请求返回的状态码一般是200的段是属于请求成功,但实际开发中,结合业务和后台接口的情况,需要自定义验证逻辑。

SessionManager.default.request(urlStr, method: .get, parameters: ["username":"Jensen","password":"123456"])
    .response { (response) in
        debugPrint(response)
    }.validate { (request, response, data) -> Request.ValidationResult in
        guard let _ = data else{
            return .failure(NSError.init(domain: "error", code: 100, userInfo: nil))
        }
        let code = response.statusCode
        if code == 404 {
            return .failure(NSError.init(domain: "error", code:101, userInfo: nil))
        }
        return .success
}

三、RequestRetrier-请求重试器

目的是控制请求的重试机制,一个典型的例子是当某个特殊的请求失败后,是否重试。

 open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    
        //......省略
   
        // Determine whether an error has occurred
        var error: Error? = error

        if request.delegate.error != nil {
            error = request.delegate.error
        }

        /// If an error occurred and the retrier is set, asynchronously ask the retrier if the request
        /// should be retried. Otherwise, complete the task by notifying the task delegate.
        if let retrier = retrier, let error = error {
            retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in
                guard shouldRetry else { completeTask(session, task, error) ; return }

                DispatchQueue.utility.after(timeDelay) { [weak self] in
                    guard let strongSelf = self else { return }

                    let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false

                    if retrySucceeded, let task = request.task {
                        strongSelf[task] = request
                        return
                    } else {
                        completeTask(session, task, error)
                    }
                }
            }
        } else {
            completeTask(session, task, error)
        }
    }
}

SessionDelegate完成请求,如果发生错误,会来到if let retrier = retrier, let error = error,判断Retrier重试闭包是否存在。
retrier 也是继承协议的处理方式,实现参考 adapter


class JNRetrier: RequestRetrier{
    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
       
        completion(true,1)//重试
        
        completion(false,0)//不重试
    }
}

1.实现RequestRetrier协议的should方法
2.指定retrierSessionManager.default.retrier = JNRetrier()3.必须要指定条件调用completion(false,0)`停止重试,否则造成死循环.

四、Result

请求返回后,需要序列化后,才是我们最终想要的。

public func response<T: DataResponseSerializerProtocol>(
    queue: DispatchQueue? = nil,
    responseSerializer: T,
    completionHandler: @escaping (DataResponse<T.SerializedObject>) -> Void)
    -> Self
{
    delegate.queue.addOperation {
        let result = responseSerializer.serializeResponse(
            self.request,
            self.response,
            self.delegate.data,
            self.delegate.error
        )

        var dataResponse = DataResponse<T.SerializedObject>(
            request: self.request,
            response: self.response,
            data: self.delegate.data,
            result: result,
            timeline: self.timeline
        )
    }
    return self
}

result是经过了 responseSerializer.serializeResponse 序列化处理的结果
result 的结果最终传入到了dataResponse

public enum Result<Value> {
    case success(Value)
    case failure(Error)
    
   // 提供成功还有失败的校验
    public var isSuccess: Bool {... }
    public var isFailure: Bool {...}
    public var value: Value? {...}
    public var error: Error? {... }
}

返回结果只有成功和失败,设计成了枚举。
Result实现了CustomStringConvertibleCustomDebugStringConvertible协议 :

extension Result: CustomStringConvertible {
    public var description: String {
       // 就是返回 "SUCCESS" 和 "FAILURE" 的标识
    }
}
extension Result: CustomDebugStringConvertible {
    public var debugDescription: String {
        // 返回标识的同时,还返回了具体内容   
    }
}

可以打印错误详细的信息。

五、Timeline 时间轴

Alamofire提供了Timeline 时间轴,开发者可以通过Timeline快速得到序列化时间,请求时间等,有助于排查问题,优化性能。

timeline: Timeline: {
 "Request Start Time": 588178187.197,
 "Initial Response Time": 588178187.537, 
"Request Completed Time": 588178187.543,
 "Serialization Completed Time": 588178187.543, 
"Latency": 0.340 secs,
 "Request Duration": 0.346 secs, 
"Serialization Duration": 0.000 secs, 
"Total Duration": 0.346 secs }

TaskDelegate的初始化中:

init(task: URLSessionTask?) {
        _task = task

        self.queue = {
            let operationQueue = OperationQueue()

            operationQueue.maxConcurrentOperationCount = 1
            operationQueue.isSuspended = true
            operationQueue.qualityOfService = .utility

            return operationQueue
        }()
    }

1.初始化OperationQueue队列
2.指定队列maxConcurrentOperationCount=1,为串行队列,保证每一个任务顺序执行
3.operationQueue.isSuspended = true队列为挂起状态.

那么这个队列在什么时候开始执行呢?

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        if let taskDidCompleteWithError = taskDidCompleteWithError {
            taskDidCompleteWithError(session, task, error)
        } else {
            if let error = error {
                if self.error == nil { self.error = error }

                if
                    let downloadDelegate = self as? DownloadTaskDelegate,
                    let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
                {
                    downloadDelegate.resumeData = resumeData
                }
            }

            queue.isSuspended = false
        }
    }

我们在TaskDelegate请求完成的代理方法中看到queue.isSuspended = false,说明是这个时候队列开始执行。

根据timeline记录和计算得出的结果,我们知道肯定会记录请求的开始时间,结束时间等等。顺着这个思路,我们猜测请求的开始时间应该是在task.resume记录的:

    open func resume() {
        guard let task = task else { delegate.queue.isSuspended = false ; return }

        if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() }

        task.resume()

        NotificationCenter.default.post(
            name: Notification.Name.Task.DidResume,
            object: self,
            userInfo: [Notification.Key.Task: task]
        )
    }

正如我们所料,startTime = CFAbsoluteTimeGetCurrent(),在这里将请求的开始时间保存在startTime

Request的初始化中:

 init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
        self.session = session

        switch requestTask {
        case .data(let originalTask, let task):
            taskDelegate = DataTaskDelegate(task: task)
            self.originalTask = originalTask
        case .download(let originalTask, let task):
            taskDelegate = DownloadTaskDelegate(task: task)
            self.originalTask = originalTask
        case .upload(let originalTask, let task):
            taskDelegate = UploadTaskDelegate(task: task)
            self.originalTask = originalTask
        case .stream(let originalTask, let task):
            taskDelegate = TaskDelegate(task: task)
            self.originalTask = originalTask
        }

        delegate.error = error
        delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
        print(delegate.queue.operationCount)
    }

在操作队列中加入任务{ self.endTime = CFAbsoluteTimeGetCurrent() },队列中加入的第一个任务,当请求结束时,队列resume,将记录结束时间到endTime

   func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
}

在请求第一次响应返回数据时,记录时间到initialResponseTime,我们看看timeline是什么时候初始化的?
response中任务到队列中,可以知道是在队列执行到这个任务时,初始化timeline

 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
    }

进入self.timeline,可以看到timeline设置:

 var timeline: Timeline {
        let requestStartTime = self.startTime ?? CFAbsoluteTimeGetCurrent()
        let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
        let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime

        return Timeline(
            requestStartTime: requestStartTime,
            initialResponseTime: initialResponseTime,
            requestCompletedTime: requestCompletedTime,
            serializationCompletedTime: CFAbsoluteTimeGetCurrent()
        )
    }

requestStartTime,requestCompletedTime,initialResponseTime是由之前保存的startTime,endTime,initialResponseTime包装而成。serializationCompletedTime是执行这个闭包的时间。

timeline初始化及数据计算:

 public init(
        requestStartTime: CFAbsoluteTime = 0.0,
        initialResponseTime: CFAbsoluteTime = 0.0,
        requestCompletedTime: CFAbsoluteTime = 0.0,
        serializationCompletedTime: CFAbsoluteTime = 0.0)
    {
        self.requestStartTime = requestStartTime
        self.initialResponseTime = initialResponseTime
        self.requestCompletedTime = requestCompletedTime
        self.serializationCompletedTime = serializationCompletedTime

        self.latency = initialResponseTime - requestStartTime
        self.requestDuration = requestCompletedTime - requestStartTime
        self.serializationDuration = serializationCompletedTime - requestCompletedTime
        self.totalDuration = serializationCompletedTime - requestStartTime
    }

为什么能够对这些时间记录,主要是通过队列同步控制实现的。在一些核心的点保存当前时间! 比如:endTime就是在下载完成那个瞬间让队列执行保存这个时间。
startTime, initialResponseTime 保存在代理中。
如果没有设置值,那么就在当时调用的时候重置当前时间
Timeline 的其他时间就是通过已知的 initialResponseTimerequestStartTimerequestCompletedTimeserializationCompletedTime 计算得出的。

上一篇下一篇

猜你喜欢

热点阅读