美文网首页
Alamofire-Request知识补充

Alamofire-Request知识补充

作者: king_jensen | 来源:发表于2019-08-23 22:55 被阅读0次

    一、请求适配器-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 计算得出的。

    相关文章

      网友评论

          本文标题:Alamofire-Request知识补充

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