Alamofire-Request知识补充
一、请求适配器-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.指定retrier
SessionManager.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
实现了CustomStringConvertible
和 CustomDebugStringConvertible
协议 :
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
的其他时间就是通过已知的 initialResponseTime
和 requestStartTime
、requestCompletedTime
、serializationCompletedTime
计算得出的。