六游的博客小站
Alamofire源码解析专栏(4)- 请求发起流程
发布于: 2020-08-08 更新于: 2020-10-20 阅读次数: 

Alamofire源码解析专栏文章地址



本篇文章主要分析Session类中的源码,主要分析Session类中如何通过对外提供的API创建一个Request对象并管理Request对象的生命周期。在之前的文章中可以了解到,Alamofire中的Request只是一个基类,我们真正使用的主要是它的各种子类,对于各种不同类型的Request,Session类中为其提供了不同的API与逻辑来管理其生命周期,但是逻辑上的差异只是源于不同类型Request所负责的不同的任务而产生的,整体上Alamofire管理Request对象的思路是不变的,所以在这篇文章中不会对每一个类型的Reuquest都进行一次分析,而是挑选其中比较基础,具有代表性的一个类型-DataRequest来做举例分析。如果你对其他类型的处理流程与逻辑有兴趣,请自行去源码文件中查找,只要你理解了DataRequest的创建以及发起的流程,再去看其他不同类型的Request的流程也就轻而易举了。

DataRequest的创建流程

首先我们来看Session类中对外提供的用来请求网络的APi方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
open func request(_ convertible: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil,
requestModifier: RequestModifier? = nil) -> DataRequest {
let convertible = RequestConvertible(url: convertible,
method: method,
parameters: parameters,
encoding: encoding,
headers: headers,
requestModifier: requestModifier)

return request(convertible, interceptor: interceptor)
}

该方法十分的简单,先是创建了一个RequestConvertible对象,然后调用另外一个request方法,直接讲方法的返回值返回。我们在专栏的第一篇文章中就提到过这种Convertible的模式,在这里RequestConvertible是一个实现了URLRequestConvertible协议的结构体,URLRequestConvertible协议定义了一类可以通过方法转化为URLRequest对象的类型。我们先来看一下这个结构体的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct RequestConvertible: URLRequestConvertible {
let url: URLConvertible
let method: HTTPMethod
let parameters: Parameters?
let encoding: ParameterEncoding
let headers: HTTPHeaders?
let requestModifier: RequestModifier?

func asURLRequest() throws -> URLRequest {
var request = try URLRequest(url: url, method: method, headers: headers)
try requestModifier?(&request)

return try encoding.encode(request, with: parameters)
}
}

注意在这个结构体的asURLRequest方法中,在返回原生的URLRequest之前,先对其进行了编码。对于编码器将参数数据编入URLRequest之中的流程不是很清楚的可以倒回去看本专栏的第一篇文章,第一篇文章中有详细解析Alamofire中编码器的定义与使用。所以我们清楚了参数数据的编码是在这一步进行的。下面我们接着上面的流程继续向下走,在Session类对外提供的request方法的最后,调用了另一个request方法,接下来我们来看这个request方法

1
2
3
4
5
6
7
8
9
10
11
12
func request(_ convertible: URLRequestConvertible, interceptor: RequestInterceptor? = nil) -> DataRequest {
let request = DataRequest(convertible: convertible,
underlyingQueue: rootQueue,
serializationQueue: serializationQueue,
eventMonitor: eventMonitor,
interceptor: interceptor,
delegate: self)

perform(request)

return request
}

DataRequest的请求发起

上面的request方法中创建了DataReqeust对象,然后对request对象调用了perform方法,最后直接返回了。看到这里我们可以直到,在使用Alamofire发起请求的时候,调用request方法发起请求之后返回的就是一个Request对象。这个方法中比较重要的是调用了perform方法,接下来来看perform方法的逻辑

1
2
3
4
5
6
7
8
9
func perform(_ request: DataRequest) {
requestQueue.async {
guard !request.isCancelled else { return }

self.activeRequests.insert(request)

self.performSetupOperations(for: request, convertible: request.convertible)
}
}

perform方法中,将刚创建的那个Request对象添加到Session的activeRequests集合中,之后对其调用了performSetupOperations方法,继续来看performSetupOperations方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
func performSetupOperations(for request: Request, convertible: URLRequestConvertible) {
let initialRequest: URLRequest
// 尝试从URLRequestConvertible对象中获取原生URLRequest,获取是顺便完成了URLRequest的参数数据编码任务
do {
initialRequest = try convertible.asURLRequest()
try initialRequest.validate()
} catch {
// 获取失败,发出错误通知
rootQueue.async { request.didFailToCreateURLRequest(with: error.asAFError(or: .createURLRequestFailed(error: error))) }
return
}
rootQueue.async { request.didCreateInitialURLRequest(initialRequest) }

guard !request.isCancelled else { return }
// 尝试获取interceptor中的adapter,这里依然使用了一个帮助函数adapter,与之前的那个retrier帮助函数一样
// 如果session与request同时都有自己的interceptor,就会将这两个interceptor的adapter重新组合为一个全新的interceptor返回
// 如果session与request两个只有一个有interceptor,就直接返回不为nil的那个
// 如果两个都为nil就直接调用didCreateURLRequest进行下一步处理,不需要再进行adapter验证
guard let adapter = adapter(for: request) else {
rootQueue.async { self.didCreateURLRequest(initialRequest, for: request) }
return
}
// 如果获取到了adapter,则使用adapter进行验证
adapter.adapt(initialRequest, for: self) { result in
do {
let adaptedRequest = try result.get()
try adaptedRequest.validate()
// 如果验证成功,同样调用didCreateURLRequest进行下一步处理
self.rootQueue.async {
request.didAdaptInitialRequest(initialRequest, to: adaptedRequest)
self.didCreateURLRequest(adaptedRequest, for: request)
}
} catch {
// 如果验证失败,发出错误通知
self.rootQueue.async { request.didFailToAdaptURLRequest(initialRequest, withError: .requestAdaptationFailed(error: error)) }
}
}
}

上面的performSetupOperations方法中主要实现的逻辑是获取DataRequest对应的urlRequest,并且将参数编码进urlRequest之后,随后对获取到的urlRequest进行adapter验证,如果验证成功会调用didCreateURLRequest方法进行下一步流程。现在我们来分析didCreateURLRequest方法的源码

1
2
3
4
5
6
7
8
9
10
11
12
func didCreateURLRequest(_ urlRequest: URLRequest, for request: Request) {
request.didCreateURLRequest(urlRequest)

guard !request.isCancelled else { return }
// 创建一个URLSessionDataTask
let task = request.task(for: urlRequest, using: session)
// 并建立task与request之间的映射关系
requestTaskMap[request] = task
request.didCreateTask(task)

updateStatesForTask(task, request: request)
}

创建了之后发起请求要用到的URLSessionDataTask之后,接着调用了updateStatesForTask方法,该方法只是处理一些异常情况,因为在正常情况下Request的发起流程只会在两种情况下被调用,第一种是发起一个新的请求,这个时候Request是刚创建的,状态应该为initialized,第二种是一个已经存在的Request请求失败需要进行重试,这个时候重新发起了请求,在Request的源码中我们可以看到在一个Request在自己要进行重试之前会先对自己的状态进行一些清空以及初始化,Request这个时候的状态为finished。所以看这个方法中,在Request的状态为initialized或finished的时候是什么都不会做的,只有出现其他一些异常情况的时候才会补充一些修复操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func updateStatesForTask(_ task: URLSessionTask, request: Request) {
request.withState { state in
switch state {
case .initialized, .finished:
break
case .resumed:
task.resume()
rootQueue.async { request.didResumeTask(task) }
case .suspended:
task.suspend()
rootQueue.async { request.didSuspendTask(task) }
case .cancelled:
task.resume()
task.cancel()
rootQueue.async { request.didCancelTask(task) }
}
}
}

此时Request已经准备就绪了,但是还差临门一脚,由DataRequest创建出来的URLSessionDataTask这个时候还没有真正的启动,Request会开始等待有新的序列化任务加入进来,如果没有序列化任务被添加进来,Request是不会启动自己的task的。我们在使用Alamofire种经常使用这样的方式来拿到请求的数据:AF.request(...).response(...),在上面的流程分析中我们已经了解到了AF.request(...)最终返回的是一个Request对象,随后调用了Reqeust的response方法,一个简单的response方法源码已经展示在下面,我们可以看到response方法做的事情实际上是通过调用Request的appendResponseSerializer方法向Request种添加一个序列化任务。在专栏文章的第二篇介绍Request时我们已经分析过了Request序列化任务添加的流程,当我们调用appendResponseSerializer方法时,会引发一系列的变化,最终会调用Request的task的resume方法,来正式启动网络请求流程,至此一个DataRequest已经被创建并启动起来了,之后只需要响应URLSessionDataDelegate中的回调,并根据回调给更新自己的状态即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extension DataRequest {
public func response(queue: DispatchQueue = .main, completionHandler: @escaping (AFDataResponse<Data?>) -> Void) -> Self {
appendResponseSerializer {
let result = AFResult<Data?>(value: self.data, error: self.error)

self.underlyingQueue.async {
let response = DataResponse(request: self.request,
response: self.response,
data: self.data,
metrics: self.metrics,
serializationDuration: 0,
result: result)

self.eventMonitor?.request(self, didParseResponse: response)

self.responseSerializerDidComplete { queue.async { completionHandler(response) } }
}
}

return self
}
}

至此这篇文章就结束了,这篇文章主要分析了一个Request对象是如何创建出来,并启动自身内部的task的。到这里本专栏可能也要告一段落了,Alamofire中的核心逻辑我们已经全部讲解完毕,只剩下一些帮助函数和不是特别重要的外围组件,这些东西可能对应着一些Alamofire中的小功能,但对于核心逻辑并没有太大的影响,大家如果有兴趣可以自己试着去读一下剩下的代码,我相信如果你将这篇专栏的前四篇文章全部认真看完并理解其中的内容,那么剩下的一些本专栏未涉及的源码对你应该是没有什么难度的。我也希望大家这么去做,因为阅读软件的源码绝不仅仅是为了了解某个软件中的逻辑与流程,因为软件有千千万万个,我们现在正在使用的Alamofire框架可能会在未来的某一个时间停止更新,然后又被另外一个新生的网络框架取代,阅读源码更重要的是:一方面要理解一些优秀软件内部所使用的设计模式与编码思想,以此来锻炼你编写优秀代码的能力;另一方面你要锻炼自己阅读源码的能力,如果在未来的工作中使用一个崭新的框架或者软件,遇到了别人没有遇到过的问题,在你毫无头绪的时候试着尝试从软件或者框架的表层入手,一步步深入地阅读其内部的源码实现,可能读着读着就突然恍然大悟:“哦!原来是这样”,在顺利解决了问题的同时也带给你很强的成就感。

--- 本文结束 The End ---