美文网首页
Alamofire-Request启动

Alamofire-Request启动

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

    一. 首先看一下request用法

        SessionManager.default.request(urlString, method: .get, parameters: nil, encoding: URLEncoding.default).response { (response) in
           debugPrint(response)
        }
    

    二. 详细参数解析

     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. url : URLConvertible,针对入参有三种处理方式:

    String,转换成URL后返回
    URL,直接使用
    URLComponents,直接返回

    //传入`String `,转为`URL`
    extension String: URLConvertible {
        public func asURL() throws -> URL {
            guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) }
            return url
        }
    }
    //如果传入的是`URL `,直接返回
    extension URL: URLConvertible {
        public func asURL() throws -> URL { return self }
    }
    
    extension URLComponents: URLConvertible {
        public func asURL() throws -> URL {
            guard let url = url else { throw AFError.invalidURL(url: self) }
            return url
        }
    }
    
    1. method默认是get,支持以下几种
    public enum HTTPMethod: String {
        case options = "OPTIONS"
        case get     = "GET"
        case head    = "HEAD"
        case post    = "POST"
        case put     = "PUT"
        case patch   = "PATCH"
        case delete  = "DELETE"
        case trace   = "TRACE"
        case connect = "CONNECT"
    }
    
    1. parameters传进来的参数
    public typealias Parameters = [String: Any]
    
    1. encoding编码格式,默认URLEncoding.default,有以下几种格式:

    URLEncoding
    JSONEncoding
    PropertyListEncoding

    1. headers请求头信息,默认nil
    public typealias HTTPHeaders = [String: String]
    
    1. 返回DataRequest

    三. 源码分析

    1. 编码
    • 首先初始化一个originalRequest
    originalRequest = try URLRequest(url: url, method: method, headers: headers)
    
    • 编码后返回request(encodedURLRequest)
     let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
    return request(encodedURLRequest)
    
    • encode编码,通过下面源码可以看到,最后都会处理参数query,主要分两种情况
    • encodesParametersInURL如果是.get, .head, .delete三种方式,进行百分号编码,放入到percentEncodedQuery
    • 其他的请求方式,设置header,然后将参数拼接到请求体httpbody
        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
        }
    
    • query 遍历参数
        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: "&")
        }
    
    • 通过ASCII排序
    • queryComponents对参数进行递归,进行编码处理后,以元组形式保存在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
        }
    
    • components.map { "\($0)=\($1)" }.joined(separator: "&") 将参数之间插入&符号
    2. 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)
            }
        }
    
    2.1 创建Task

    let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
    借助DataRequest内部的结构体Requestable创建Task

    通过urlRequest初始化Requestable
    然后再用originalTask创建Task
    返回queue.sync { session.dataTask(with: urlRequest) }

        struct Requestable: TaskConvertible {
            let urlRequest: URLRequest
            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)
                }
            }
        }
    
    2.2 创建request:

    let request = DataRequest(session: session, requestTask: .data(originalTask, task))

    • 调用DataRequest的父类Request的初始化方法.通过传入枚举的方式,初始化参数同时保存信息,此时传入的是.data(let originalTask, let task)
     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() }
    
    • 初始化taskDelegate,调用super.init(task: task)
        override init(task: URLSessionTask?) {
            mutableData = Data()
            progress = Progress(totalUnitCount: 0)
            super.init(task: task)
        }
    
    • 调用父类初始化方法init(task: URLSessionTask?)保存_task,初始化队列
        init(task: URLSessionTask?) {
            _task = task
            self.queue = {
                let operationQueue = OperationQueue()
                operationQueue.maxConcurrentOperationCount = 1
                operationQueue.isSuspended = true
                operationQueue.qualityOfService = .utility
                return operationQueue
            }()
        }
    
    • 保存self.originalTask = originalTask
    2.3 保存request:

    delegate[task] = request,将request保存到SessionDelegate中,便于SessionDelegate管理

    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
            }
        }
    
    2.4 启动request.resume()
    if startRequestsImmediately { request.resume() }
    

    以上就是request启动流程,通过上面流程分析,可知:

    SessionDelegate是总的任务管理者,具体执行的时候,通过不同的request如:DataRequest,DownloadRequest,UploadRequest等去处理,实现解耦的目的。

    相关文章

      网友评论

          本文标题:Alamofire-Request启动

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