在前面源码探索中,
SessionManager
管理Request
和SessionDelegate
的建立,并经过task
绑定Request
和SessionDelegate
对象;Request
负责请求的参数的配置,以及task
不一样任务的建立,建立链接外部(发送请求对象)和TaskDelegate
的方法,经过闭包参数,获取TaskDelegate
代理事件的内容;TaskDelegate
代理事件是由SessionDelegate
经过task
移交的。总结图:前端
以上处理的目的是对任务作分层处理,使结构清晰。json
在Request
文件下还存在一个协议RequestAdapter
。在Manager
中建立调用。以下:swift
open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
var originalRequest: URLRequest?
do {
originalRequest = try urlRequest.asURLRequest()
let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
let request = DataRequest(session: session, requestTask: .data(originalTask, task))
delegate[task] = request
if startRequestsImmediately { request.resume() }
return request
} catch {
return request(originalRequest, failedWith: error)
}
}
复制代码
联系上下文,adapter
并无被初始化,怎么回事呢?下面看一下是如何定义的:api
/// A type that can inspect and optionally adapt a `URLRequest` in some manner if necessary.
public protocol RequestAdapter {
/// Inspects and adapts the specified `URLRequest` in some manner if necessary and returns the result.
///
/// - parameter urlRequest: The URL request to adapt.
///
/// - throws: An `Error` if the adaptation encounters an error.
///
/// - returns: The adapted `URLRequest`.
func adapt(_ urlRequest: URLRequest) throws -> URLRequest
}
复制代码
一个协议内部定义了一个方法,上面定义能够以某种方式检查并适应URLRequest
,实际是告诉咱们,根据须要遵循该协议并实现该方法。一脸懵逼,实现它干吗呢?其实也不难猜想,既然给咱们该类型,确定是方便咱们设置参数,如token、device、vision
等等这些公共参数,其实能够设置的,那下面就来试试。网络
class MyAdapter: RequestAdapter{
func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
var request = urlRequest
request.setValue("hibotoken", forHTTPHeaderField: "token")
request.setValue("device", forHTTPHeaderField: "iOS")
request.setValue("vision", forHTTPHeaderField: "1.0.0")
return request
}
}
复制代码
下面设置adapter并发送一个请求:session
let urlStr = "http://onapp.yahibo.top/public/?s=api/test/list"
let url = URL.init(string: urlStr)!
Alamofire.SessionManager.default.adapter = MyAdapter()
Alamofire.request(url,method: .post,parameters: ["page":"1","size":"20"]).responseJSON {
(response) in
switch response.result{
case .success(let json):
print("json:\(json)")
break
case .failure(let error):
print("error:\(error)")
break
}
}
复制代码
SessionManager
中定义了adapter
对象,这里就对其赋值一个实现了adapt
方法的子类对象 这里在请求前在adapt
中设置了请求头,那么就运行一下,经过抓包看看公共参数是否添加成功:添加成功,开发中的参数之后就能够单独使用该方法进行管理了。闭包
class redireatAdapter: RequestAdapter{
func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
let newURLRequest = URLRequest.init(url: URL.init(string: "http://onapp.yahibo.top/public/?s=api/test")!)
return newURLRequest
}
}
复制代码
直接修改原请求地址,重定向至其余地址。并发
为何会添加公共参数,或重定向?app
代码追踪,追踪到最终使用位置以下:框架
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
do {
let urlRequest = try self.urlRequest.adapt(using: adapter)
return queue.sync { session.dataTask(with: urlRequest) }
} catch {
throw AdaptError(error: error)
}
}
func adapt(using adapter: RequestAdapter?) throws -> URLRequest {
guard let adapter = adapter else { return self }
return try adapter.adapt(self)
}
复制代码
这里调用了该方法,这里判断了adapter
是否存在,不存在直接使用前面建立并设置好参数的URLRequest
对象,若是存在则adapter
调用adapt
方法,将当前URLRequest
对象传出去加工处理。
开发中常常会根据不一样的状态码来处理,好比开发中须要将某一结果定义为错误请求,在error
中来作处理,那么在该框架中咱们可使用validate
来从新验证,并定义请求结果。代码以下:
let urlStr = "http://onapp.yahibo.top/public/?s=api/test/list2"
let url = URL.init(string: urlStr)!
Alamofire.request(url,method: .post,parameters: ["page":"1","size":"20"]).responseJSON {
(response) in
switch response.result{
case .success(let json):
print("json:\(json)")
break
case .failure(let error):
print("error:\(error)")
break
}
}.validate{ (request, response, data) -> Request.ValidationResult in
print(response)
guard let _ = data else {
return .failure(NSError(domain: "没有数据啊", code: 0, userInfo: nil))
}
guard response.statusCode == 200 else {
return .failure(NSError(domain: "是否是哪弄错了", code: response.statusCode, userInfo: nil))
}
return .success
}
复制代码
validate
验证方法,根据具体需求添加验证逻辑statusCode != 200
认为是错误请求 经过以上试用,咱们对Alamofire
又有了更多的了解,不管是监听请求进度仍是这种验证均以链式调用为主,方便快捷。不少状况下,若是网络请求失败,咱们是有从新请求的需求,那么该框架也提供了这样的方法,请求失败都会调用代理方法:urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
。而框架就在该代理方法中作了以下处理:
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)
}
复制代码
retrier
是否被定义若是定义则调用should
方法retrier
是一个继承自RequestRetrier
协议的类对象RequestRetrier
/// A type that determines whether a request should be retried after being executed by the specified session manager
/// and encountering an error.
public protocol RequestRetrier {
/// Determines whether the `Request` should be retried by calling the `completion` closure.
///
/// This operation is fully asynchronous. Any amount of time can be taken to determine whether the request needs
/// to be retried. The one requirement is that the completion closure is called to ensure the request is properly
/// cleaned up after.
///
/// - parameter manager: The session manager the request was executed on.
/// - parameter request: The request that failed due to the encountered error.
/// - parameter error: The error encountered when executing the request.
/// - parameter completion: The completion closure to be executed when retry decision has been determined.
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion)
}
复制代码
RequestAdapter
同样,须要定义类并实现方法建立子类并继承协议,实现协议方法以下:
class MyRetrier: RequestRetrier{
var count: Int = 0
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
if count<3 {
completion(true,2)
count += 1
}else{
completion(false,2)
}
}
}
复制代码
completion
有两个参数shouldRetry
为是否请求,timeDelay
为延时请求的延时时间,这里设置为2秒下面就能够设置一个错误链接发送请求尝试一下:
let urlStr = "http://onapp.yahibo.top/public/?s=api/test/list2"
let url = URL.init(string: urlStr)!
Alamofire.SessionManager.default.retrier = MyRetrier()
Alamofire.request(url,method: .post,parameters: ["page":"1","size":"20"]).responseJSON {
(response) in
switch response.result{
case .success(let json):
print("json:\(json)")
break
case .failure(let error):
print("error:\(error)")
break
}
}.validate{ (request, response, data) -> Request.ValidationResult in
print(response)
guard let _ = data else {
return .failure(NSError(domain: "没有数据啊", code: 10086, userInfo: nil))
}
if response.statusCode == 404 {
return .failure(NSError(domain: "密码错误", code: response.statusCode, userInfo: nil))
}
return .success
}
复制代码
RequestAdapter
使用方法一致,须要配置SessionManager的retrier
属性Alamofire
对请求到的数据进行了处理再返回给咱们,以上请求咱们都调用了responseJSON
方法来获取最终数据,下面看一下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
)
}
复制代码
联系上文可知responseJSON
是DataRequest
的一个扩展方法,继承自Request
类,所以能够进行链式调用。该方法内部继续调用了response
方法。方法以下:
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
)
dataResponse.add(self.delegate.metrics)
(queue ?? DispatchQueue.main).async { completionHandler(dataResponse) }
}
return self
}
复制代码
DataResponse
对象中以上其实并无看到咱们熟悉的序列化,再继续搜索,找到以下代码:
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
对象中Result
对象最终封装至DataResponse
对象中来管理从上面代码可以发现response
对象管理了请求过程当中全部参数:
var dataResponse = DataResponse<T.SerializedObject>(
request: self.request,
response: self.response,
data: self.delegate.data,
result: result,
timeline: self.timeline
)
复制代码
所以在请求结果中,咱们可以很方便的拿到全部咱们须要的信息。
为何有时间轴,在网络请求中,咱们须要准确的知道请求耗时,以便于前端或后台作优化处理。下面就看一下Alamofire
的时间轴是如何设计的。
首先咱们可以看到,任务是在队列中执行的:
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
do {
let urlRequest = try self.urlRequest.adapt(using: adapter)
return queue.sync { session.dataTask(with: urlRequest) }
} catch {
throw AdaptError(error: error)
}
}
复制代码
队列是在SessionManager
中建立,Manager
真是什么都管啊。代码以下:
let queue = DispatchQueue(label: "org.alamofire.session-manager." + UUID().uuidString)
复制代码
UUID
紧接着初始化TaskDelegate
对象。以下:
open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
var originalRequest: URLRequest?
do {
originalRequest = try urlRequest.asURLRequest()
let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
let request = DataRequest(session: session, requestTask: .data(originalTask, task))
delegate[task] = request
if startRequestsImmediately { request.resume() }
return request
} catch {
return request(originalRequest, failedWith: error)
}
}
复制代码
经过.data(originalTask, task)传入任务task,来初始化TaskDelegate对象以下:
self.queue = {
let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 1
operationQueue.isSuspended = true
operationQueue.qualityOfService = .utility
return operationQueue
}()
复制代码
一、startTime-记录发起请求时间
任务的建立与执行在Request中进行,代码以下:
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]
)
}
复制代码
resume
在SessionManager
中调用执行task
不存在,说明任务已结束,队列启动执行其余任务startTime
作了判空操做二、endTimer-记录请求结束时间
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() }
}
复制代码
上面代码作了一个初始化,为何说是结束时间呢,由于队列为同步队列,上次请求任务结束后才会执行。即请求完成后,代码以下:
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
}
}
复制代码
queue.isSuspended = false
恢复队列,恢复后上面提到的记录时间任务便可加入到队列中执行三、initialResponseTime-初始化响应时间
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
}
复制代码
四、TimeLine-时间轴设置
在响应初始化中,初始化时间轴:
extension DataRequest {
@discardableResult
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
中初始化时间轴,对前面的时间记录作统一管理:
extension Request {
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()
)
}
}
复制代码
时间轴初始化,计算请求间隔,序列化时间间隔:
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
}
复制代码
时间轴TimeLine
记录了请求过程当中的操做时间点,并计算了每部操做的时间间隔,在请求结束后封装至Response
中。这里经过队列来同步请求中的操做,以保证startTime、endTime
的准确性,其余时间记录是在请求代理回调中设置。
TimeLine: