Alamofire源碼學習(二): Session

往期導航:

Alamofire源碼學習目錄合集ios

先簡單介紹基礎文件Alamofire.swift:

/// Reference to `Session.default` for quick bootstrapping and examples.
public let AF = Session.default

/// Current Alamofire version. Necessary since SPM doesn't use dynamic libraries. Plus this will be more accurate.
let version = "5.4.0"
複製代碼

就這兩個常量, 一個是Session類的一個單例, 一個標記了當前版本號.bootstrap

Session類是ALamofire的核心類, 封裝了URLSession類, 管理全部的請求調度.

1、相關屬性註解:

/// 一個默認的單例
    public static let `default` = Session()

    /// 持有一個URLSession, 用來建立請求Task, 注意不能跟本對象持有的URLSessionTask產生交互, 不然會影響Alamofire內部邏輯
    public let session: URLSession
    
    /// 處理URLSession代理, URLSessionTaskDelegate, 以及請求攔截等邏輯
    public let delegate: SessionDelegate
    
    /// 內部回調執行以及狀態更新的隊列,必須是串行隊列
    public let rootQueue: DispatchQueue
    
    /// 是否在Request建立的時候就馬上發送, 該屬性用來統一管理Request建立時的startImmediately參數, 默認true
    public let startRequestsImmediately: Bool
    
    /// 異步建立Request的隊列,默認是rootQueue
    public let requestQueue: DispatchQueue
    
    /// 解析response的隊列, 默認是rootQueue
    public let serializationQueue: DispatchQueue
    
    /// 請求攔截器接口,是RequestAdapter跟RequestRetrier的結合, 默認nil
    public let interceptor: RequestInterceptor?
    
    /// 證書信任器接口, 默認nil
    public let serverTrustManager: ServerTrustManager?
    
    /// 重定向處理器接口, 默認nil
    public let redirectHandler: RedirectHandler?
    
    /// 緩存管理接口, 默認nil
    public let cachedResponseHandler: CachedResponseHandler?
    
    /// 事件監測管理器類,處理請求生命週期各階段的事件, 默認用下面的defaultEventMonitors以及傳入的事件檢測器初始化
    public let eventMonitor: CompositeEventMonitor
    
    /// 默認的事件監測器接口列表,只有一個通知事件監測器
    public let defaultEventMonitors: [EventMonitor] = [AlamofireNotifications()]

    /// 結構體, 用來保存Request跟URLSessiontask映射關係, 提供了各類方法來存取task跟request以及數量判斷, 爲空判斷
    var requestTaskMap = RequestTaskMap()
    
    /// 當前正在請求的Request集合
    var activeRequests: Set<Request> = []
    
    /// 等待成功的回調
    var waitingCompletions: [URLSessionTask: () -> Void] = [:]
複製代碼

2、初始化

Session有兩個初始化方法, 一個必要初始化方法, 一個便捷初始化方法.swift

public init(session: URLSession, delegate: SessionDelegate, rootQueue: DispatchQueue, startRequestsImmediately: Bool = true, requestQueue: DispatchQueue? = nil, serializationQueue: DispatchQueue? = nil, interceptor: RequestInterceptor? = nil, serverTrustManager: ServerTrustManager? = nil, redirectHandler: RedirectHandler? = nil, cachedResponseHandler: CachedResponseHandler? = nil, eventMonitors: [EventMonitor] = []) {
        // Alamofire不支持後臺下載(爲啥嘞?看後面是支持的)
        precondition(session.configuration.identifier == nil,
                     "Alamofire does not support background URLSessionConfigurations.")
        // URLSession的Queue跟rootQueue必須同樣
        precondition(session.delegateQueue.underlyingQueue === rootQueue,
                     "Session(session:) initializer must be passed the DispatchQueue used as the delegateQueue's underlyingQueue as rootQueue.")

        self.session = session
        self.delegate = delegate
        self.rootQueue = rootQueue
        self.startRequestsImmediately = startRequestsImmediately
        // 默認用rootQueue作target建立請求響應解析隊列
        self.requestQueue = requestQueue ?? DispatchQueue(label: "\(rootQueue.label).requestQueue", target: rootQueue)
        self.serializationQueue = serializationQueue ?? DispatchQueue(label: "\(rootQueue.label).serializationQueue", target: rootQueue)
        // 四個默認nil的屬性
        self.interceptor = interceptor
        self.serverTrustManager = serverTrustManager
        self.redirectHandler = redirectHandler
        self.cachedResponseHandler = cachedResponseHandler
        // 根據傳入的事件監聽器以及默認的監聽器初始化CompositeEventMonitor對象
        eventMonitor = CompositeEventMonitor(monitors: defaultEventMonitors + eventMonitors)
        // 把組合監聽器對象丟給SessionDelegate對象用來管理task
        delegate.eventMonitor = eventMonitor
        // 請求時Session變化的處理
        delegate.stateProvider = self
    }
複製代碼

便捷初始化方法使用了默認的URLSessionConfiguration以及請求隊列來初始化URLSession, SessionDelegate, rootQueueapi

3、deinit處理:

deinit {
        finishRequestsForDeinit()
        session.invalidateAndCancel()
    }
    func finishRequestsForDeinit() {
        //挨個通知還沒完成的Request 返回sessionDeinitialized錯誤
        requestTaskMap.requests.forEach { request in
            rootQueue.async {
                request.finish(error: AFError.sessionDeinitialized)
            }
        }
    }
複製代碼

4、所有請求處理:

//對全部正在執行的請求執行一個閉包
    public func withAllRequests(perform action: @escaping (Set<Request>) -> Void) {
        rootQueue.async {
            action(self.activeRequests)
        }
    }
    //取消所有請求
    public func cancelAllRequests(completingOnQueue queue: DispatchQueue = .main, completion: (() -> Void)? = nil) {
        withAllRequests { requests in
            requests.forEach { $0.cancel() }
            queue.async {
                completion?()
            }
        }
    }
複製代碼

5、請求初始化:

先定義了RequestConvertibleRequestEncodableConvertible結構體實現URLRequestConvertible協議, 用來使用不一樣參數建立URLRequestConvertible對象緩存

// 用來變換URLRequest對象的閉包
    public typealias RequestModifier = (inout URLRequest) throws -> Void
    //普通的request轉換器, 使用ParameterEncoding協議對象來編碼參數
    struct RequestConvertible: URLRequestConvertible {
        let url: URLConvertible//url
        let method: HTTPMethod//方法
        let parameters: Parameters?//參數
        let encoding: ParameterEncoding//參數編碼對象, 默認URL編碼
        let headers: HTTPHeaders?//請求頭
        let requestModifier: RequestModifier?
    }
    //參數符合Encodable協議的轉換器, 使用ParameterEncoder協議對象編碼參數
    struct RequestEncodableConvertible<Parameters: Encodable>: URLRequestConvertible {
        let url: URLConvertible
        let method: HTTPMethod
        let parameters: Parameters?
        let encoder: ParameterEncoder
        let headers: HTTPHeaders?
        let requestModifier: RequestModifier?
    }
複製代碼

對於普通request, streamRequest, downloadRequest, 都有三個建立請求的方法:sass

  1. 先使用參數建立RequestConvertible對象, 而後轉換爲URLRequestConvertible後使用3來建立對應的Request對象
  2. 使用RequestConvertible建立, 邏輯同1
  3. 使用URLRequestConvertible對象+rootQueue+serializationQueue+eventMonitor建立Request對象, 並指定RequestDelegate爲self用來處理URLSessionConfiguration, 清理工做以及重試邏輯
  • downloadRequest有一個額外的斷點續傳的方法, 使用已下載的Data來初始化DownloadRequest對象

對於UploadRequest, 又定義了ParameterlessRequestConvertible結構體實現URLRequestConvertible協議, 特色是沒有參數.
接着定義Upload結構體實現UploadConvertible用來封裝request與uploadable
對於UploadRequest的建立使用的都是ParameterlessRequestConvertible對象, 有共計X個建立UploadRequest的方法:安全

  1. 使用data + 請求基礎參數, 先生成ParameterlessRequestConvertible對象,轉2
  2. 使用data, URLRequestConvertible對象, 生成Uploadable.data對象, 轉11
  3. 使用fileURL + 請求基礎參數, 先生成ParameterlessRequestConvertible對象, 轉4
  4. 使用fileURL, ParameterlessRequestConvertible對象, 生成Uploadable.file對象, 轉11
  5. 使用InputStream + 基礎參數 轉6
  6. 使用InputStream, ParameterlessRequestConvertible, 生成Uploadable.stream對象,轉11
  7. 使用多表單閉包+請求基礎參數, 執行閉包生成MultipartFormData, ParameterlessRequestConvertible, 轉換爲URLRequestConvertible, 轉10
  8. 使用多表單閉包+URLRequestConvertible, 執行閉包, 轉10
  9. 使用MultipartFormData對象+基礎請求參數, 生成ParameterlessRequestConvertible, MultipartUpload, 轉12
  10. 使用MultipartFormData對象+URLRequestConvertible生成MultipartUpload, 轉12
  • 如下爲intenal api, 外部沒法訪問
  1. 使用Uploadable + URLRequestConvertible, 生成Upload, 轉12 (在Session中未用到)
  2. 使用UploadConvertible協議對象生成UploadRequest併發送

注意:
  1. upload方法共有12個, 本質上upload方法分爲3種: data(內存), fileURL(磁盤), InputStream(磁盤), 7-10均爲處理多表單數據, 參數中帶有一個encodingMemoryThreshold參數, 用來決定編碼內存限制, 當表單數據大小大於該限制時, 編碼將把表單數據存到磁盤, 使用iostream來處理, 避免內存太高. 生成的Uploadable的類型根據表單編碼類型來肯定.
  2. 全部的upload的url都沒有query參數, 因此用的是ParameterlessRequestConvertible結構體來轉換URLRequestConvertible協議對象.
  3. upload的12個方法 都是互相調用, 最終的數據編碼, 都在對應的Request模塊

6、準備發送請求

主入口爲:markdown

// MARK: Perform

    /// Starts performing the provided `Request`.
    ///
    /// - Parameter request: The `Request` to perform.
    func perform(_ request: Request) {
        rootQueue.async {
            //先在rootQueue中判斷是否請求被取消
            guard !request.isCancelled else { return }
            //塞入到正在請求的Request集合中
            self.activeRequests.insert(request)
            //在requestQueue隊列發送請求
            self.requestQueue.async {
                // 子類必須先case不然就會被識別爲父類了
                switch request {
                case let r as UploadRequest: 
                     // UploadRequest是DataRequest的子類
                    self.performUploadRequest(r)
                    //先建立Uploadable
                    //而後在rootQueue告知事件監聽器didCreateUploadable, 而後調用performSetupOperations方法
                    //建立失敗的話先在rootQueue告知監視器didFailToCreateUploadable, 錯誤消息爲createUploadableFailed
                    //而後在request中決定是否重試
                case let r as DataRequest: 
                    self.performDataRequest(r)
                    //直接調performSetupOperations方法
                case let r as DownloadRequest: 
                    self.performDownloadRequest(r)
                    //對request的downloadable進行斷定
                    //若是是新建下載, 直接調用performSetupOperations方法
                    //若是是斷點續傳, 在rootQueue調用didReceiveResumeData方法, 詳見下方斷點續傳部分
                case let r as DataStreamRequest: 
                    self.performDataStreamRequest(r)
                    //直接調performSetupOperations方法
                default: 
                    fatalError("Attempted to perform unsupported Request subclass: \(type(of: request))")
                }
            }
        }
    }
複製代碼

方法performSetupOperations接受兩個參數: Request對象以及URLRequestConvertible協議對象, 後者來自於request.convertible屬性. 處理成功後, 會調用didCreateURLRequest方法來更新狀態session

func performSetupOperations(for request: Request, convertible: URLRequestConvertible) {
        //當前在requestQueue
        dispatchPrecondition(condition: .onQueue(requestQueue))
        //URLRequestConvertible生成的URLRequest
        let initialRequest: URLRequest

        do {
            initialRequest = try convertible.asURLRequest()
            //檢測請求是否有效(get請求不能帶body參數)
            try initialRequest.validate()
        } catch {
            //出錯的話就在rootQueue隊列上報錯誤
            rootQueue.async { request.didFailToCreateURLRequest(with: error.asAFError(or: .createURLRequestFailed(error: error))) }
            return
        }
        //在rootQueue通知request,初始化URLRequest成功(使用MutableState記錄狀態, 告知事件監聽器didCreateInitialURLRequest)
        rootQueue.async { request.didCreateInitialURLRequest(initialRequest) }
        
        guard !request.isCancelled else { return }
        //檢查是否有請求適配器, 內部邏輯:
        //1. 判斷request的攔截器跟Session的攔截器都不爲空的話, 就返回生成組合攔截器
        //2. 返回request攔截器或者Session攔截器
        guard let adapter = adapter(for: request) else {
            //沒有攔截器的話直接通知
            rootQueue.async { self.didCreateURLRequest(initialRequest, for: request) }
            return
        }
        //使用攔截器中的適配器來預處理請求
        adapter.adapt(initialRequest, for: self) { result in
            do {
                let adaptedRequest = try result.get()
                try adaptedRequest.validate()
                //通知處理完成
                self.rootQueue.async {
                    request.didAdaptInitialRequest(initialRequest, to: adaptedRequest)
                    self.didCreateURLRequest(adaptedRequest, for: request)
                }
            } catch {
                //任何錯誤都拋出requestAdaptationFailed錯誤
                self.rootQueue.async { request.didFailToAdaptURLRequest(initialRequest, withError: .requestAdaptationFailed(error: error)) }
            }
        }
    }
複製代碼

7、建立URLSessionTask, 發送請求

當建立請求完成, 攔截適配器處理完成以後, 就會來到這裏的邏輯, 除了斷點續傳的請求會執行didReceiveResumeData方法, 其餘的幾個請求都會執行didCreateURLRequest方法, 而後最終都會調用updateStatesForTask方法來更新閉包

func didCreateURLRequest(_ urlRequest: URLRequest, for request: Request) {
        dispatchPrecondition(condition: .onQueue(rootQueue))
        //通知建立request成功
        request.didCreateURLRequest(urlRequest)

        guard !request.isCancelled else { return }
        //建立URLSessionTask, 基類Request爲實現該方法, 幾個子類各自實現
        let task = request.task(for: urlRequest, using: session)
        //寫入Session的Request-Task數據對裏保存
        requestTaskMap[request] = task
        //建立URLSessionTask成功後, request作的相關處理, 線程安全地保存task, 並通知事件監聽器建立task成功
        request.didCreateTask(task)

        updateStatesForTask(task, request: request)
    }
    //基本邏輯跟上面類似, 區別就是建立task的方法不一樣, 使用已下載Data建立URLSessionDownloadTask
    func didReceiveResumeData(_ data: Data, for request: DownloadRequest) {
        dispatchPrecondition(condition: .onQueue(rootQueue))

        guard !request.isCancelled else { return }

        let task = request.task(forResumeData: data, using: session)
        requestTaskMap[request] = task
        request.didCreateTask(task)

        updateStatesForTask(task, request: request)
    }
    //上面兩個方法完成後會調用該方法, 開始開始發送請求
    func updateStatesForTask(_ task: URLSessionTask, request: Request) {
        //確認是在rootQueue隊列更新URLSessionTask的狀態
        dispatchPrecondition(condition: .onQueue(rootQueue))
        //根據request的狀態來更新對應task的狀態
        request.withState { state in
            switch state {
            case .initialized, .finished:
                // 初始化或者請求完成, 啥也不幹
                break
            case .resumed:
                //發送請求
                task.resume()
                //告知request task開始請求
                rootQueue.async { request.didResumeTask(task) }
            case .suspended:
                //請求掛起
                task.suspend()
                rootQueue.async { request.didSuspendTask(task) }
            case .cancelled:
                //先恢復task 再取消, 保證task被取消
                task.resume()
                task.cancel()
                rootQueue.async { request.didCancelTask(task) }
            }
        }
    }
複製代碼

而後等待request各自的RequestDelegate去處理請求落地的邏輯便可

8、請求適配器以及重試器:

//這兩個邏輯都同樣, 使用 Interceptor 類來組合Session默認的以及每一個請求單獨的 適配器/重試器
    func adapter(for request: Request) -> RequestAdapter? {
        if let requestInterceptor = request.interceptor, let sessionInterceptor = interceptor {
            return Interceptor(adapters: [requestInterceptor, sessionInterceptor])
        } else {
            return request.interceptor ?? interceptor
        }
    }

    func retrier(for request: Request) -> RequestRetrier? {
        if let requestInterceptor = request.interceptor, let sessionInterceptor = interceptor {
            return Interceptor(retriers: [requestInterceptor, sessionInterceptor])
        } else {
            return request.interceptor ?? interceptor
        }
    }
複製代碼

9、RequestDelegate

每個Request對象都持有一個RequestDelegate代理對象用來獲取建立Request時的URLSessionConfiguration以及全局startImmediately, 而且處理錯誤重試與延遲重試邏輯, 指向建立Request的Session, 所以在Session中實現了RequestDelegate協議

10、SessionStateProvider

Session持有SessionDelegate用來處理Task的代理, SessionDelegate又持有一個RequestTaskMap代理指向Session, 來處理當Task狀態變動時的狀態處理(由於Session持有一個RequestTaskMap對象來儲存Request跟NSURLSessionTask的映射關係), 所以當SessionDelegate響應URLSessionDelegate以及各個NSURLSessionTask的代理回調時, 會回調到Session中的SessionStateProvider相關協議方法中,來根據Task取得對應的Request, 並判斷是否請求完成斷開映射關係。

//全部操做都在rootQueue進行
extension Session: SessionStateProvider {
    func request(for task: URLSessionTask) -> Request? {
        dispatchPrecondition(condition: .onQueue(rootQueue))
        //根據task獲取Request
        return requestTaskMap[task]
    }

    func didGatherMetricsForTask(_ task: URLSessionTask) {
        dispatchPrecondition(condition: .onQueue(rootQueue))
        //task成功獲取任務指標後的行爲
        //在RequestTaskMap結構體中處理, 檢測是否須要斷開request跟task的連接
        let didDisassociate = requestTaskMap.disassociateIfNecessaryAfterGatheringMetricsForTask(task)
        //waitingCompletions閉包來自於task請求成功後設置的後續操做, 若是成功獲取任務指標後, 執行後續操做, 至此請求完成
        if didDisassociate {
            waitingCompletions[task]?()
            waitingCompletions[task] = nil
        }
    }

    func didCompleteTask(_ task: URLSessionTask, completion: @escaping () -> Void) {
        dispatchPrecondition(condition: .onQueue(rootQueue))
        //檢測是否須要斷開request跟task的連接
        let didDisassociate = requestTaskMap.disassociateIfNecessaryAfterCompletingTask(task)
        //若是沒有後續邏輯, 直接執行完成回調, 不然把回調保存在waitingCompletions字典中, 等待檢查任務指標後再判斷是否要斷開連接
        if didDisassociate {
            completion()
        } else {
            waitingCompletions[task] = completion
        }
    }

    func credential(for task: URLSessionTask, in protectionSpace: URLProtectionSpace) -> URLCredential? {
        dispatchPrecondition(condition: .onQueue(rootQueue))
        //HTTPS證書處理
        return requestTaskMap[task]?.credential ??
            session.configuration.urlCredentialStorage?.defaultCredential(for: protectionSpace)
    }

    func cancelRequestsForSessionInvalidation(with error: Error?) {
        dispatchPrecondition(condition: .onQueue(rootQueue))
        //當URLSession出錯時, 並拋出錯誤 
        requestTaskMap.requests.forEach { $0.finish(error: AFError.sessionInvalidated(error: error)) }
    }
}
複製代碼

10、總結:

  • 整個Session類的做用就是:
    1. 初始化並管理URLSession, 持有SessionDelegate對象管理NSURLSession的相關代理方法。
    2. 初始化不一樣Request對象,分爲三步:
      • 根據不一樣的參數, 派發到不一樣方法建立不一樣的Request子類對象
      • 使用攔截適配器(包括Session帶的默認/全局攔截器 + 每一個Request帶的專有攔截器)預處理Request對象
      • 調用Request的方法建立URLSessionTask子類,將Request與URLSessionTask連接配對
    3. 調用resume發送task
    4. 實現RequestDelegate協議提供建立Request對象時的兩個控制參數,以及處理重試邏輯
    5. 實現SessionStateProvider協議處理Request跟Task的狀態
  • Session只管建立Request, 發送Task, 管理Request跟Task的映射, Request的建立, 響應的處理, 緩存, HTTPS, OAuth2認證等將在後續學習了源碼以後再慢慢補全

我的理解記錄~若有錯誤歡迎評論指出,很是感謝~

相關文章
相關標籤/搜索