六游的博客小站
Alamofire源码解析专栏(3)- SessionDelegate
发布于: 2020-08-07 更新于: 2020-10-20 阅读次数: 

Alamofire源码解析专栏文章地址



这篇文章我们来分析SessionDelegate相关的源码,主要涉及到Session中的一些属性类型源码的分析以及SessionDelegaet类中对于各种系统原生代理协议的实现

在文章的开头,我再将专栏的第一篇文章中放出来的Session类中的成员属性代码贴出来,因为该文章很大部分都是在分析Session类中的实例变量类型的源码

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
/* Session类的共享单例对象 */
public static let `default` = Session()

/* 一个Session中持有一个URLSession对象,底层使用这个URLSession发起各种请求 */
public let session: URLSession
/* 处理URLSession的代理方法的一个类,其本身实现了URLSessionDelegate、URLSessionDataDelegate、URLSessionDownloadDelegate等多个原生Session代理协议 */
public let delegate: SessionDelegate
/* rootQueue用来执行所有内部的回调方法和状态更新操作,必须是串行队列(为了保证对资源操作的有序性) */
public let rootQueue: DispatchQueue
/* 下面这个值决定实例是否对所有创建的request自动调用resume(),如果为true则会在request创建完成后自动立马调用resume方法 */
public let startRequestsImmediately: Bool
/* 用来异步创建URLRequest的queue,默认情况直接使用上面的rootQueue,如果创建URLRequest成为了你的系统的瓶颈可以尝试引入另外一个附加的queue */
public let requestQueue: DispatchQueue
/* 用来序列化请求响应数据的queue,默认情况直接使用上面的rootQueue,如果对response的序列化成为了你的系统的瓶颈,可以尝试引入一个附加的queue */
public let serializationQueue: DispatchQueue
/* 全局的请求拦截器,可以在请求发出前或者响应前做出一些判断,并根据需要拦截或者放行请求。 */
public let interceptor: RequestInterceptor?
/* 全局的证书验证器 */
public let serverTrustManager: ServerTrustManager?
/* 全局的请求重定向处理器,当一个请求被系统代理告知需要重定向,而请求又没有自己的一个重定向处理器的时候,就会调用全局的处理器来处理重定向 */
public let redirectHandler: RedirectHandler?
/* 全局的缓存处理器,响应请求所产生的缓存 */
public let cachedResponseHandler: CachedResponseHandler?
/* 事件监听器,是一个集中的事件分发中心 */
public let eventMonitor: CompositeEventMonitor
/* 默认的事件监听器订阅者列表,事件监听器中的所有订阅者由默认的订阅者加初始化传入的监听者组成 */
public let defaultEventMonitors: [EventMonitor] = [AlamofireNotifications()]

/* 内部的map,存储着创建的Request与对应的URLSessionTask之间的映射 */
var requestTaskMap = RequestTaskMap()
/* 一个set,存储这当前活跃的(还未被关闭)的所有request */
var activeRequests: Set<Request> = []
/* 一个字典,记录着某个task完成时需要执行的回调 */
var waitingCompletions: [URLSessionTask: () -> Void] = [:]

事件监听中心CompositeEventMonitor

事件监听中心CompositeEventMonitor负责接收Alamofire系统中的各种通知,并将这些通知发送给全部的订阅者,监听中心给订阅者发送通知是通过调用函数的方式来发送的,所以在我们阅读CompositeEventMonitor源码之前,先看一下Alamofire中对订阅者的定义,Alamofre中使用一个协议EventMonitor来表示一个订阅者角色,协议中声明了各种通知对应的方法,如果监听中心想要发送一个通知给订阅者,就直接调用订阅者的对应方法完成通知的传递。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public protocol EventMonitor {
/// 返回监听者在接收到通知之后需要在哪个队列上运行相对应的方法
var queue: DispatchQueue { get }

// URLSession变为Invalid状态,并且发生了错误
func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?)
// URLSession收到了服务器信任挑战
func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge)
// 某个URLSession下的task上传了新的数据
func urlSession(_ session: URLSession,
task: URLSessionTask,
didSendBodyData bytesSent: Int64,
totalBytesSent: Int64,
totalBytesExpectedToSend: Int64)
// 以下省略……
}

EventMonitor协议中定义了非常多的方法,一个方法对应一个通知事件,这些事件中一部分是与URLSessionDelegate或者URLSessionDataDelegate等一些系统原生网络代理中的方法相关,另外一部分是与我们上一篇文章中提到过的Reques他的生命周期相关。除了这个协议之外,因为该协议中定义的方法数量十分巨大,并且大多数情况下监听者只会同时关注其中的几个通知,所以为了便于实现协议,Alamofire还会协议中的每一个方法提供了一个默认实现,这些默认实现大部分由空实现构成。

1
2
3
4
5
6
7
8
9
10
11
12
extension EventMonitor {
/// The default queue on which `CompositeEventMonitor`s will call the `EventMonitor` methods. `.main` by default.
public var queue: DispatchQueue { .main }

// MARK: Default Implementations

public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {}
public func urlSession(_ session: URLSession,
task: URLSessionTask,
didReceive challenge: URLAuthenticationChallenge) {}
// 省略n行代码……
}

下面来看事件监听中心CompositeEventMonitor的源码

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
public final class CompositeEventMonitor: EventMonitor {
// 事件监听中心向监听者们分发事件所使用的队列
public let queue = DispatchQueue(label: "org.alamofire.compositeEventMonitor", qos: .background)
// 事件监听中心中会存储所有的监听者
let monitors: [EventMonitor]
init(monitors: [EventMonitor]) {
self.monitors = monitors
}
// 最关键的是这个方法,这个方法的逻辑很简单,就是接受一个闭包作为通知的分发函数
// 然后遍历所有的监听者,对其应用传入的闭包,将通知分发给每一个监听者
func performEvent(_ event: @escaping (EventMonitor) -> Void) {
queue.async {
for monitor in self.monitors {
monitor.queue.async { event(monitor) }
}
}
}
// 除此之外,监听者EventMonitor协议中定义的每一个方法在CompositeEventMonitor类中都有一个与之对应的方法
// 调用CompositeEventMonitor类中的这些方法,相当于向通知中心发送了一条该方法对应的通知,通知中心会把这条通知下发下去
// 例如下面这个例子
public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
// 方法中调用了performEvent对收到的通知进行下发
performEvent { $0.urlSession(session, didBecomeInvalidWithError: error) }
}
// 省略n行代码……
}

RequestTaskMap

Alamofire中许多关于网络请求状态的更新都是基于原生URLSession的各种代理方法回调来完成的,原生的回调方法中会告诉你是哪一个Task的状态发生了改变,但是在Alamofire中将请求封装为了Request类,所以就需要有一个可以通过task找到与之对应的Request的方法。然后就有了RequestTaskMap,这个类中存储了Request与原生Task之间的映射。

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
struct RequestTaskMap {
private typealias Events = (completed: Bool, metricsGathered: Bool)
// 从task到Reqeust的映射
private var tasksToRequests: [URLSessionTask: Request]
// 从Request到task的映射
private var requestsToTasks: [Request: URLSessionTask]
private var taskEvents: [URLSessionTask: Events]

var requests: [Request] {
Array(tasksToRequests.values)
}

init(tasksToRequests: [URLSessionTask: Request] = [:],
requestsToTasks: [Request: URLSessionTask] = [:],
taskEvents: [URLSessionTask: (completed: Bool, metricsGathered: Bool)] = [:]) {
self.tasksToRequests = tasksToRequests
self.requestsToTasks = requestsToTasks
self.taskEvents = taskEvents
}
// 下标方法,建立\删除\获取 Reqeust到task的映射
subscript(_ request: Request) -> URLSessionTask? {
get { requestsToTasks[request] }
set {
guard let newValue = newValue else {
guard let task = requestsToTasks[request] else {
fatalError("RequestTaskMap consistency error: no task corresponding to request found.")
}

requestsToTasks.removeValue(forKey: request)
tasksToRequests.removeValue(forKey: task)
taskEvents.removeValue(forKey: task)

return
}

requestsToTasks[request] = newValue
tasksToRequests[newValue] = request
taskEvents[newValue] = (completed: false, metricsGathered: false)
}
}
// 下标方法,建立\删除\获取 task到Reqeust的映射
subscript(_ task: URLSessionTask) -> Request? {
get { tasksToRequests[task] }
set {
guard let newValue = newValue else {
guard let request = tasksToRequests[task] else {
fatalError("RequestTaskMap consistency error: no request corresponding to task found.")
}

tasksToRequests.removeValue(forKey: task)
requestsToTasks.removeValue(forKey: request)
taskEvents.removeValue(forKey: task)

return
}

tasksToRequests[task] = newValue
requestsToTasks[newValue] = task
taskEvents[task] = (completed: false, metricsGathered: false)
}
}
}

RequestTaskMap除了上面源码中我们看到的存取Request与Task之间的映射关系的功能之外,还会额外记录每个task的完成状态

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
39
struct RequestTaskMap {
private typealias Events = (completed: Bool, metricsGathered: Bool)
// 字典中每一个task对应一个Events,Events中记录着该task是否已经complete,以及是否已经完成metrics数据的收集
private var taskEvents: [URLSessionTask: Events]
// 要想看懂下面的两个方法,就需要先了解一些概念
// task的complete事件以及metrics收集完成事件都是由原生系统中的代理发出的
// 然而这两个事件到达的顺序是不一定的,哪个事件先到达是随机的
// 这两个事件有一个没到,就说明这个task还没完全结束,还需要保留
// 如果两个事件都到了,那么就说明这个task已经彻底没有用处了,可以理解为task的生命周期已经结束了
// 这个时候我们就称这个task处于disassociate状态,断开联系了
// 下面两个方法就对应这两个事件,方法会返回某个task在接收到对应事件之后是否已经disassociate
mutating func disassociateIfNecessaryAfterGatheringMetricsForTask(_ task: URLSessionTask) -> Bool {
guard let events = taskEvents[task] else {
fatalError("RequestTaskMap consistency error: no events corresponding to task found.")
}

switch (events.completed, events.metricsGathered) {
case (_, true): fatalError("RequestTaskMap consistency error: duplicate metricsGatheredForTask call.")
case (false, false): taskEvents[task] = (completed: false, metricsGathered: true); return false
case (true, false): self[task] = nil; return true
}
}

mutating func disassociateIfNecessaryAfterCompletingTask(_ task: URLSessionTask) -> Bool {
guard let events = taskEvents[task] else {
fatalError("RequestTaskMap consistency error: no events corresponding to task found.")
}

switch (events.completed, events.metricsGathered) {
case (true, _): fatalError("RequestTaskMap consistency error: duplicate completionReceivedForTask call.")
#if os(watchOS) // watchOS doesn't gather metrics, so unconditionally remove the reference and return true.
default: self[task] = nil; return true
#else
case (false, false): taskEvents[task] = (completed: true, metricsGathered: false); return false
case (false, true): self[task] = nil; return true
#endif
}
}
}

SessionDelegate类

由于要想通过代理处理原生URLSession中所有行为的回调是非常繁琐与复杂的,所以Alamofire新创建了一个类来实现原生Session的各种代理协议。Session是Alamofire中的核心管理类,SessionDelegate在处理代理回调的时候必然会涉及到需要与Session交互的逻辑。之前在分析Request时,Request也需要与Session进行交互,其做法时定义一个代理协议由Session本身实现,然后自身持有这个代理属性。现在这个SessionDelegate也使用了类似的方式处理这个问题。首先我们来看SessionStateProvider协议,虽然协议名称中没有包含delegate这个单词,但实际上从作用上来说,这就是一个代理协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protocol SessionStateProvider: AnyObject {
// 获取Session中用来处理服务器信任挑战的ServerTrustManager
var serverTrustManager: ServerTrustManager? { get }
// 获取Session中用来处理重定向的RedirectHandler
var redirectHandler: RedirectHandler? { get }
// 获取Session中用来响应缓存的CachedResponseHandler
var cachedResponseHandler: CachedResponseHandler? { get }
// 拿到与task对应的Request类
func request(for task: URLSessionTask) -> Request?
// 通知Session对task的metrics已经完成了收集工作
func didGatherMetricsForTask(_ task: URLSessionTask)
// 通知Session task已经完成
func didCompleteTask(_ task: URLSessionTask, completion: @escaping () -> Void)
func credential(for task: URLSessionTask, in protectionSpace: URLProtectionSpace) -> URLCredential?
// 通知Session因为URLSession失效而出错
func cancelRequestsForSessionInvalidation(with error: Error?)
}

接下来我们来看一下Session类是如何实现这个协议的

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
39
40
41
42
43
44
45
46
47
48
49
50
51
extension Session: SessionStateProvider {
// 直接在自己属性中的requestTaskMap中查找
func request(for task: URLSessionTask) -> Request? {
dispatchPrecondition(condition: .onQueue(rootQueue))

return requestTaskMap[task]
}
// 下面的这两个通知方法,一个是通知task已经complete,一个是通知task的metrics已经收集完毕
// 由于在原生系统中对于同一个task来说这两个事件没有固定的到达顺序
// 但是SessionDelegate提供的闭包必须要等到这两个事件都已经完成之后才能调用,如果metrics还没有收集完毕之前就调用这个闭包会导致Request直接结束,无法正常完成metrics的收集
// 所以就有了以下两个方法中的逻辑
func didCompleteTask(_ task: URLSessionTask, completion: @escaping () -> Void) {
dispatchPrecondition(condition: .onQueue(rootQueue))
// 这里的didDisassociate表明task是否完全解挂,也就是是否两个通知都已经收到过了
let didDisassociate = requestTaskMap.disassociateIfNecessaryAfterCompletingTask(task)
// 这里如果返回true,代表metrics收集完成事件比complete事件到达的更早
// 说明接受到complete事件时可以直接执行闭包了
if didDisassociate {
completion()
// 如果返回false,代表此时metrics收集完成的事件还没有到来
// 所以先将该闭包存起来,等到metrics收集完成之后再调用
} else {
waitingCompletions[task] = completion
}
}
func didGatherMetricsForTask(_ task: URLSessionTask) {
dispatchPrecondition(condition: .onQueue(rootQueue))
let didDisassociate = requestTaskMap.disassociateIfNecessaryAfterGatheringMetricsForTask(task)
// 这里如果返回true,代表complete事件要更早到来,闭包已经被放入Session中做暂存了
// 直接拿出来执行并清空就可以了
if didDisassociate {
waitingCompletions[task]?()
waitingCompletions[task] = nil
}
// 如果为false,代表complete事件还没来,就什么都不用做,等待complete事件到来就可以了
}

// 优先返回task对应的Request中的URLCredential对象,如果没有就返回Session中的默认URLCredential对象
func credential(for task: URLSessionTask, in protectionSpace: URLProtectionSpace) -> URLCredential? {
dispatchPrecondition(condition: .onQueue(rootQueue))

return requestTaskMap[task]?.credential ??
session.configuration.urlCredentialStorage?.defaultCredential(for: protectionSpace)
}
// 如果被通知URLSession除了问题,那么就通知Session中管理的所有Request网络请求失败
func cancelRequestsForSessionInvalidation(with error: Error?) {
dispatchPrecondition(condition: .onQueue(rootQueue))

requestTaskMap.requests.forEach { $0.finish(error: AFError.sessionInvalidated(error: error)) }
}
}

接下来我们来看SessionDelegate中原生代理协议的实现

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
39
40
extension SessionDelegate: URLSessionDataDelegate {
/// 当一个task接收到服务器传过来的数据时
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
// 首先向通知中心发送通知
eventMonitor?.urlSession(session, dataTask: dataTask, didReceive: data)
// 之后获取到task对应的request,并调用request的对应的方法来更新request的状态
// 还记得我们在Request那一篇文章中讲到的,Request中有一类方法是专门来与原生代理通知做交互的吗
// 这些方法就是在这里使用的
if let request = request(for: dataTask, as: DataRequest.self) {
request.didReceive(data: data)
} else if let request = request(for: dataTask, as: DataStreamRequest.self) {
request.didReceive(data: data)
} else {
assertionFailure("dataTask did not find DataRequest or DataStreamRequest in didReceive")
return
}
}
}

extension SessionDelegate: URLSessionDownloadDelegate {
/// 当一个downloadTask接收到新的数据发送时
open func urlSession(_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didResumeAtOffset fileOffset: Int64,
expectedTotalBytes: Int64) {
// 首先向通知中心发送通知
eventMonitor?.urlSession(session,
downloadTask: downloadTask,
didResumeAtOffset: fileOffset,
expectedTotalBytes: expectedTotalBytes)
// 接着获取到task对应的request
guard let downloadRequest = request(for: downloadTask, as: DownloadRequest.self) else {
assertionFailure("downloadTask did not find DownloadRequest.")
return
}
// 然后调用request的对应方法来更新request状态,这里是更新了DownloadRequest的下载进度
downloadRequest.updateDownloadProgress(bytesWritten: fileOffset,
totalBytesExpectedToWrite: expectedTotalBytes)
}
}

上面我们只罗列出了SessionDelegate对原生代理协议的实现的一小部分,SessionDelegate中对于系统原生协议中的几乎所有方法都有对应的实现,但这些方法的基本套路都一样,都是根据通知传递过来的task找到与之对应的request,然后再对request做出某些状态更新的操作,所以这里就不一一列举了,如果读者对这些代码感兴趣,可以自行去源代码文件中查看,代码在SessionDelegate.swift文件中。

SessionDelegate证书验证过程

在了解具体的流程之前,我们需要先了解几个类型。首先是Session类中的一个成员属性的类型:ServerTrustManager,该类型管理着Alamofire系统中所有与证书验证相关的证书验证器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
open class ServerTrustManager {
public let allHostsMustBeEvaluated: Bool
// 存储证书验证器的字典,以服务器Host为键,与之对应的证书验证器为值
// 所以服务器每个Host都会对应一个证书验证器
public let evaluators: [String: ServerTrustEvaluating]

public init(allHostsMustBeEvaluated: Bool = true, evaluators: [String: ServerTrustEvaluating]) {
self.allHostsMustBeEvaluated = allHostsMustBeEvaluated
self.evaluators = evaluators
}
/// 获取某个host对应的证书验证器
open func serverTrustEvaluator(forHost host: String) throws -> ServerTrustEvaluating? {
guard let evaluator = evaluators[host] else {
// 如果没找到,并且规定了所有Host必须经过证书验证器验证,就会直接报错,请求会直接失败
if allHostsMustBeEvaluated {
throw AFError.serverTrustEvaluationFailed(reason: .noRequiredEvaluator(host: host))
}
// 如果没找到但没有规定所有Host必须经过证书验证器验证,就会返回nil,使用原生系统默认逻辑处理证书验证
return nil
}
// 如果找到了就将对应的证书验证器返回上层
return evaluator
}
}

接下来是关于证书验证器的定义,其在Alamofire中仅仅是一个简单的协议

1
2
3
4
5
6
public protocol ServerTrustEvaluating {
// 通过该方法传入一个SecTrust和需要进行验证的服务器的Host
// 如果抛出错误代表验证不通过,会终止请求
// 如果正常return代表证书验证通过
func evaluate(_ trust: SecTrust, forHost host: String) throws
}

然后我们来分析SeverDelegate类在证书验证流程中的一个关键helper方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func attemptServerTrustAuthentication(with challenge: URLAuthenticationChallenge) -> ChallengeEvaluation {
let host = challenge.protectionSpace.host
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
let trust = challenge.protectionSpace.serverTrust
else {
return (.performDefaultHandling, nil, nil)
}
do {
// 通过stateProvider拿到指定Host对应的证书验证器
// 如果返回为nil,就使用系统默认的逻辑处理此次证书验证
guard let evaluator = try stateProvider?.serverTrustManager?.serverTrustEvaluator(forHost: host) else {
return (.performDefaultHandling, nil, nil)
}
// 如果拿到了证书验证器,就调用证书验证器的方法进行验证
try evaluator.evaluate(trust, forHost: host)
// 如果没有抛出错误,说明验证成功,返回成功信息
return (.useCredential, URLCredential(trust: trust), nil)
} catch {
// 如果抛出了错误,说明验证失败,返回失败信息
return (.cancelAuthenticationChallenge, nil, error.asAFError(or: .serverTrustEvaluationFailed(reason: .customEvaluationFailed(error: error))))
}
}

然后我们继续来分析SessionDelegate对于原生系统代理中的证书验证方法的实现

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
open func urlSession(_ session: URLSession,
task: URLSessionTask,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
// 首先向通知中心发送一个通知
eventMonitor?.urlSession(session, task: task, didReceive: challenge)
// ChallengeEvaluation是一个使用typealias定义的元组:
// typealias ChallengeEvaluation = (disposition: URLSession.AuthChallengeDisposition, credential: URLCredential?, error: AFError?)
// 第一个参数URLSession.AuthChallengeDisposition表示系统接下来要如何处置此次证书验证
// 第二个参数是在disposition为.useCredential时才需要附加的一个UserCredential对象
// 第三个参数是当证书验证失败的时候产生的错误
let evaluation: ChallengeEvaluation
// 根据不同的authenticationMethod,调用不同的helper方法,获取到证书验证的结果
switch challenge.protectionSpace.authenticationMethod {
case NSURLAuthenticationMethodServerTrust:
evaluation = attemptServerTrustAuthentication(with: challenge)
case NSURLAuthenticationMethodHTTPBasic, NSURLAuthenticationMethodHTTPDigest, NSURLAuthenticationMethodNTLM,
NSURLAuthenticationMethodNegotiate, NSURLAuthenticationMethodClientCertificate:
evaluation = attemptCredentialAuthentication(for: challenge, belongingTo: task)
default:
evaluation = (.performDefaultHandling, nil, nil)
}
// 如果验证结果出错了,那么就通过stateProvider通知对应的request请求失败
if let error = evaluation.error {
stateProvider?.request(for: task)?.didFailTask(task, earlyWithError: error)
}
// 如果验证成功了,就调用系统代理方法中提供的回调,继续网络请求
completionHandler(evaluation.disposition, evaluation.credential)
}

ServerManage在接收到服务器发来的证书验证请求时,会将任务交给Session去执行,而Session会将任务交给其成员属性serverTrustManager去执行,最终serverTrustManager会在其内部寻找此次请求的Host字符串对应的一个证书验证器,并将任务交给证书验证器负责。

在使用Alamofire来进行网络请求时,如果遇到自签名证书或者单个证书服务于多个域名等需求时,需要你创建遵循ServerTrustEvaluating协议的证书验证器类,在func evaluate(_ trust: SecTrust, forHost host: String) throws方法中,去验证每次请求的正确性。之后创建一个ServerTrustManager的实例,将一系列你创建的证书验证器与要验证的Host字符串的字典放入到你创建的serverTrustManager中去,最后将新创建的serverTrustManager通过实例化方法传入,构建出来一个新的Session,并在之后的网络请求中使用这个Session

好了本篇的内容到这里就结束了,系列专栏未完待续……

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