美文网首页Swift
Alamofire-Request解析

Alamofire-Request解析

作者: king_jensen | 来源:发表于2019-08-21 17:13 被阅读0次

    请求过程

    Alamofire中的静态方法都是调用SessionManager里面的方法,SessionManagerdefault里存放着默认的session,而SessionDelegate则实现了session的代理。
    SessionDelegate类中:

    1.声明了许多闭包,如果你想自定义接收响应的逻辑可以实现闭包

     open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)?
    
        /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`.
        open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
    
        /// Overrides all behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)` and requires the caller to call the `completionHandler`.
        open var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
    

    2.看下回调的代理方法的实现

       open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
            if let dataTaskDidReceiveData = dataTaskDidReceiveData {
                dataTaskDidReceiveData(session, dataTask, data)
            } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
                delegate.urlSession(session, dataTask: dataTask, didReceive: data)
            }
        }
    

    如果外界实现闭包dataTaskDidReceiveData,就直接回调闭包dataTaskDidReceiveData,不再下发到具体的代理DataTaskDelegate执行。
    否则将任务下发到DataTaskDelegate去执行。DataTaskDelegate是怎么处理的?

     func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
            if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
    
            if let dataTaskDidReceiveData = dataTaskDidReceiveData {
                dataTaskDidReceiveData(session, dataTask, data)
            } else {
                if let dataStream = dataStream {
                    dataStream(data)
                } else {
                    mutableData.append(data)
                }
    
                let bytesReceived = Int64(data.count)
                totalBytesReceived += bytesReceived
                let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
    
                progress.totalUnitCount = totalBytesExpected
                progress.completedUnitCount = totalBytesReceived
    
                if let progressHandler = progressHandler {
                    progressHandler.queue.async { progressHandler.closure(self.progress) }
                }
            }
        }
    

    Request初探

    明白Alamofire中一个请求的过程,是非常有必要的。先看下边的代码:

    SessionManager.default.request(myGetUrlString, method: .get, parameters: ["array":getJsonFromArray(array)])
                .response { (response) in
                    debugPrint(response)
            }
    

    上边的代码是最简单的一个请求,我们看看Alamofire.request中究竟干了什么?

       open func request(
            _ url: URLConvertible,
            method: HTTPMethod = .get,
            parameters: Parameters? = nil,
            encoding: ParameterEncoding = URLEncoding.default,
            headers: HTTPHeaders? = nil)
            -> DataRequest
        {
            var originalRequest: URLRequest?
    
            do {
                originalRequest = try URLRequest(url: url, method: method, headers: headers)
                let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
                return request(encodedURLRequest)
            } catch {
                return request(originalRequest, failedWith: error)
            }
        }
    

    1.创建URLRequest
    2.encodingURLEncoding类型的.encoding.encode会调用到

      public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
            var urlRequest = try urlRequest.asURLRequest()
    
            guard let parameters = parameters else { return urlRequest }
    
            if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {
                guard let url = urlRequest.url else {
                    throw AFError.parameterEncodingFailed(reason: .missingURL)
                }
    
                if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
                    let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
                    urlComponents.percentEncodedQuery = percentEncodedQuery
                    urlRequest.url = urlComponents.url
                }
            } else {
                if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
                    urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
                }
    
                urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
            }
    
            return urlRequest
        }
    

    urlRequestURLRequestConvertible协议类型,URLRequestConvertible协议的目的是对URLRequest进行自定义的转换。
    判断method,如果method.get, .head, .delete类型,需要urlComponents.percentEncodedQuery进行百分号编码,并且query(parameters)参数拼接。
    如果是post,需要设置请求头application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type,然后拼接参数query(parameters),将参数转换为二进制并赋值到urlRequest.httpBody

    我们看看query(parameters)的实现:

    private func query(_ parameters: [String: Any]) -> String {
            var components: [(String, String)] = []
    
            for key in parameters.keys.sorted(by: <) {
                let value = parameters[key]!
                components += queryComponents(fromKey: key, value: value)
            }
            return components.map { "\($0)=\($1)" }.joined(separator: "&")
        }
    

    (1)将所有参数的keys按照ASCII码排序
    (2)创建components数组,遍历parameters,取出每一对ket-value
    (3)调用queryComponents(key,value),返回一个元祖,并添加到数组components

     public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
            var components: [(String, String)] = []
    
            if let dictionary = value as? [String: Any] {
                for (nestedKey, value) in dictionary {
                    components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
                }
            } else if let array = value as? [Any] {
                for value in array {
                    components += queryComponents(fromKey: arrayEncoding.encode(key: key), value: value)
                }
            } else if let value = value as? NSNumber {
                if value.isBool {
                    components.append((escape(key), escape(boolEncoding.encode(value: value.boolValue))))
                } else {
                    components.append((escape(key), escape("\(value)")))
                }
            } else if let bool = value as? Bool {
                components.append((escape(key), escape(boolEncoding.encode(value: bool))))
            } else {
                components.append((escape(key), escape("\(value)")))
            }
    
            return components
        }
    

    如果是dictionaryarray类型,继续递归调用queryComponents,如果是NSNumber,Bool,String类型,将数据添加到components

    (4).components.map { "\($0)=\($1)" }转化为key-vaue的字符串数组,然后调用joined(separator: "&"),&作为分隔符,连接成我们需要的数据并返回

    Request内部关系

     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)
            }
        }
    

    1.首先创建一个Requestable结构体对象,使用Requestable创建task
    为什么要有Requestable这个层,不直接把task·放到DataRequest? (1)我们知道taskrequest是一一对应的关系,关系是平级的,如果直接把task放到DataRequest`中,体现的是一种归属关系。
    (2)降低耦合性。任务分层,架构思路更清晰

    2.delegate[task] = request,绑定taskrequest , 方便SessionDelegate下发任务,用task直接检索到request
    delegate[task] = request我们在OC中看到这种写法都是数组或者字典,而这里的delegate是一个类。这是Swift的下标法。我们看看实现:

      var requests: [Int: Request] = [:]
       private let lock = NSLock()
     open subscript(task: URLSessionTask) -> Request? {
            get {
                lock.lock() ; defer { lock.unlock() }
                return requests[task.taskIdentifier]
            }
            set {
                lock.lock() ; defer { lock.unlock() }
                requests[task.taskIdentifier] = newValue
            }
        }
    

    类可以重写下标,支持get,get方法。这种方式SessionDelegate不直接持有Request可以降低耦合。

    3.if startRequestsImmediately { request.resume() },满足startRequestsImmediately,就会发起请求。 open var startRequestsImmediately: Bool = true,startRequestsImmediately默认会true。为什么要这么设计?而不直接调用request.resume()。这里是为了让用户可以通过在外界对startRequestsImmediately控制,而达到控制请求发不发起。

    4.Request有许多不同的子类实现。DataRequest,DownloadRequest,UploadRequest,StreamRequest。这里是任务细分。

    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)
                }
            }
    

    DataRequest初始化:调用父类Requestinit方法

     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() }
        }
    

    1.使用枚举requestTask标识不同的类型。是工厂设计模式,根据用户传进来的枚举标识,走不同的初始化。
    2.保存相关信息originalTask(Requestable类型)
    requestTask有如下几种:

     enum RequestTask {
            case data(TaskConvertible?, URLSessionTask?)
            case download(TaskConvertible?, URLSessionTask?)
            case upload(TaskConvertible?, URLSessionTask?)
            case stream(TaskConvertible?, URLSessionTask?)
        }
    

    这里的根据枚举类型的不同,会初始化不同的代理,DataTaskDelegate,DownloadTaskDelegate,UploadTaskDelegate,TaskDelegate。疑问:为什么已经有SessionDelegate,还要有这些代理?这些代理与SessionDelegate是什么关系?

    上面的请求流程我们有提到:
    1.SessionDelegatesession的代理,那么所有的代理方法都会首先来到SessionDelegate
    2.SessionDelegate下发响应到具体的任务实现者SessionDelegate,其实就是整体局部关系.
    3.所有的响应都会来到SessionDelegate,如果SessionDelegate将所有的响应都自己处理,那将会很复杂,混乱.根据不同的需求将具体实现下发到对应的代理中处理,耦合性大大降低,架构的分层更加明显。

    如上述请求过程中提到的:

       open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
            if let dataTaskDidReceiveData = dataTaskDidReceiveData {
                dataTaskDidReceiveData(session, dataTask, data)
            } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
                delegate.urlSession(session, dataTask: dataTask, didReceive: data)
            }
        }
    

    SessionDelegate可以快速方便的通过dataTask 获取到request。然后拿到具体的delegate,如DataTaskDelegate。然后下发具体的任务,至于DataTaskDelegate中做了什么SessionDelegate根本就不需要知道。

    总结:


    999.png

    相关文章

      网友评论

        本文标题:Alamofire-Request解析

        本文链接:https://www.haomeiwen.com/subject/yggpsctx.html