Alamofire之Request基本流程

2019-08-23  本文已影响0人  越来越胖了

首先来看以下两段代码:

Alamofire.request("https://httpbin.org/get").responseJSON { response in
            print(" request ==   \(response.request as Any)")  // 原始的URL请求    https://httpbin.org/get
            print("response =    \(response.response as Any)") // HTTP URL响应   一些请求头信息
            print("data =    \(response.data as Any)")     // 服务器返回的数据      376 bytes   376字节
            print("result =    \(response.result as Any)")   // 响应序列化结果,在这个闭包里,存储的是JSON数据
            
            if let JSON = response.result.value {
                print("JSON: \(JSON)")
            }
        }

SessionManager.default.request("https://httpbin.org/get").responseJSON { response in
            print(" request ==   \(response.request as Any)")  // 原始的URL请求    https://httpbin.org/get
            print("response =    \(response.response as Any)") // HTTP URL响应   一些请求头信息
            print("data =    \(response.data as Any)")     // 服务器返回的数据      376 bytes   376字节
            print("result =    \(response.result as Any)")   // 响应序列化结果,在这个闭包里,存储的是JSON数据
            
            if let JSON = response.result.value {
                print("JSON: \(JSON)")
            }
        }

其实上面的两种写法是一样的,现在就第一种来分析下Alamofire进行网络请求的基本流程:

1.SessionManager.default.request

public func request(
    _ url: URLConvertible,
    method: HTTPMethod = .get,
    parameters: Parameters? = nil,
    encoding: ParameterEncoding = URLEncoding.default,
    headers: HTTPHeaders? = nil)
    -> DataRequest
{
    return SessionManager.default.request(
        url,
        method: method,
        parameters: parameters,
        encoding: encoding,
        headers: headers
    )
}

进行了很多的默认值的设置,这也是为什么我们可以传一个url直接请求的原因,SessionManager.default如下:

 public static let `default`: SessionManager = {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders

        return SessionManager(configuration: configuration)
    }()

调用了SessionManager(configuration: configuration) 初始化方法; 初始化具体干了什么鬼操作呢?

public init(
        configuration: URLSessionConfiguration = URLSessionConfiguration.default,
        delegate: SessionDelegate = SessionDelegate(),
        serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
    {
        self.delegate = delegate
        self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)

        commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
    }
public let delegate: SessionDelegate

下面是commonInit的方法实现:

 private func commonInit(serverTrustPolicyManager: ServerTrustPolicyManager?) {
        session.serverTrustPolicyManager = serverTrustPolicyManager

        delegate.sessionManager = self

        delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
            guard let strongSelf = self else { return }
            DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
        }
    }

delegate.sessionManager = self , SessionManager类中创建了一个SessionDelegate类.SessionDelegate类里面传入了SessionManager,形成一个回环,然后通过weak打破循环引用.

delegate.sessionDidFinishEventsForBackgroundURLSession这个是后台下载完成的回调,这个前面章节介绍过了.

初始化过程总结一下就是:

  • URLSession的配置
  • 代理的移交,
  • 创建了一个后台下载完成的回调闭包

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

我们就传了一个URL URLConvertible = https://httpbin.org/get 其他的使用默认值(swift的可以这样干😑);
创建URLRequest并进行相应的信息配置:

originalRequest = try URLRequest(url: url, method: method, headers: headers)

通过url,method,headers创建URLRequest(通过url创建request,用request创建task);

let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)参数处理,Parameters是要传递什么参数,比如登录的username,password:

 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
    }

通过ifelse知道主要把请求分成两种情况处理,进入到if中的可以 是.get, .head, .delete,其他为else方法实现:

 private func encodesParametersInURL(with method: HTTPMethod) -> Bool {
       …
        switch method {
        case .get, .head, .delete:
            return true
        default:
            return false
        }
    }

得到完整的请求percentEncodedQuery赋值给urlComponents.percentEncodedQuery,URLComponents 是一个结构体,可以保存信息;

下面对着get,post两种情况进行分析:

1.get

let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)

分析:


private func query(_ parameters: [String: Any]) -> String {
        var components: [(String, String)] = []
//以下为分情况递归遍历,拿到传入的所有的数据🍎
        for key in parameters.keys.sorted(by: <) {
            let value = parameters[key]!
//key 和value进行组装,返回的数组里面的放的是元祖🍎
            components += queryComponents(fromKey: key, value: value)
        }
       //例: username=XXXX&passward=OOOO     XXXX,OOOO 为百分号编码结果🍎
        return components.map { "\($0)=\($1)" }.joined(separator: "&")
    }
//递归处理🍎
    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
    }

2.post

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)

可以看到,post是把学习存储在了httpBody,同时数据是进行了处理的;一般我们传递给服务器时,后台也会要求我们把数据转成json发送,比如下面这样:

extension ViewController {
    // options: .prettyPrinted  使用空白和缩进使输出更具可读性的写入选项
    fileprivate func getJsonFromArray(_ array:Array<Any>) -> String{
        let data = try? JSONSerialization.data(withJSONObject: array, options: .prettyPrinted)
        return String(data: data!, encoding: .utf8) ?? ""
    }
}

post其他具体的就不再深究了.

数据处理完成后调用request(encodedURLRequest),传入处理好的数据:


open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
        var originalRequest: URLRequest?

        do {
            //取出之前配置的URLRequest;🍎
            originalRequest = try urlRequest.asURLRequest()
            //设置类的结构体中的成员urlRequest的值,其实就是保存了URLRequest🍎
            let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
            ////创建task任务🍎
            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)
        }
    }

说明:

  • let originalTask = DataRequest.Requestable(urlRequest: originalRequest!);设置类的结构体中的成员urlRequest的值,其实就是默认初始化一个结构体,并设置结构体中的属性urlRequest的值,也就是保存了URLRequest;所以可以理解为这里是一个储存型结构体.结构体的一种常规使用方法,值得我们学习;
  • 为什么要这样干?其实也可以直接DataRequest类中定义一个task属性,通过DataRequest去创建,但是这样的话,task相关的所有操作都需要DataRequest去处理,而且DataRequesttask不应该有一个包含关系,他们更像是同一级的,由DataRequest去创建task,会导致DataRequest过重;
  • 而既然这样那为什么不定义一个task类去处理呢 ? 因为我们需要保存urlRequest的值;
  • 最后,我们的DataRequesttask是有对应关系的,通过结构体把这个依赖打破了,降低耦合,那taskDataRequest的对应关系怎么办? 关联方法---> delegate[task] = request;

let task = try originalTask.task(session: session, adapter: adapter, queue: queue),调用结构体中的task方法,创建task任务,实现方法中调用了{ session.dataTask(with: urlRequest) } 同步创建task,这里传入了一个adapt,这个参数下一章节再介绍.

//这个是DataRequest中的结构体Requestable🍎
 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)
            }
        }
    }

以上主要就是创建task任务,同时在结构体中保存了URLRequest

Request.swift中的类其实有4种,都是,Request的子类,——根据不同需求会调用不同的类的方法;如下载时,我们调用的不再是DataRequest类,而是DownloadRequest类,同时所有的子类继承了Request的方法,因为这些方法是每个子类所必须要有的功能,所以写在一起了这里.这种写法也是值得大家去参考的,主要是共有的功能的抽离形成父类.

let request = DataRequest(session: session, requestTask: .data(originalTask, task))其实就是调用父类的初始化方法,这一句才是DataRequest的初始化,所以很明显能感觉到,前面的task的创建和DataRequest是分离的.而创建的类型通过枚举requestTask传参决定,这就是工厂设计模式,同时这里也涉及到枚举的常规所用,这些都是非常值得我们学习的地方.

open class Request {
…
 enum RequestTask {
        case data(TaskConvertible?, URLSessionTask?)
        case download(TaskConvertible?, URLSessionTask?)
        case upload(TaskConvertible?, URLSessionTask?)
        case stream(TaskConvertible?, URLSessionTask?)
    }
…
init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
        self.session = session
//data传参决定创建类型,典型的工厂设计🍎
        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() }
    }
…
}

  • DataRequest的初始化 还创建了一个taskDelegate,让self.originalTask = 创建的taskdelegate,把两个delegate关联就在这个方法里面;
  • let originalTask: TaskConvertible? , self是Request类,self.originalTask其实就是Request中的TaskConvertible对象;

我们前面提到了一个代理的移交,回顾一下: URLSession的代理———>移交给了SessionManager类中的 SessionDelegate类;当请求的数据返回时,通过系统的URLSession的代理拿到数据;因为代理移交的关系,会响应SessionDelegate类里面的方法:

open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        /// Executed after it is determined that the request is not going to be retried

//一个闭包🍎
        let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in
            guard let strongSelf = self else { return }
//self是SessionDelegate类, 这里的意思是,对外的方法taskDidComplete用户是否实现?如果实现了,把数据传给外界.🍎
//实现方法就是这样:SessionManager.default.delegate.taskDidComplete= {…}🍎
            strongSelf.taskDidComplete?(session, task, error)
//strongSelf[task]下标法去到Request,然后任务下发,而delegate就是taskDelegate(内部定义的属性),上面我们说到了,self.originalTask = 创建的taskdelegate,🍎
//也就意味着,  .urlSession方法会调用TaskDelegate的urlSession方法🍎
            strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error)
//这下面是通知的响应,流程和上面是一样的,先不管🍎
            var userInfo: [String: Any] = [Notification.Key.Task: task]

            if let data = (strongSelf[task]?.delegate as? DataTaskDelegate)?.data {
                userInfo[Notification.Key.ResponseData] = data
            }

            NotificationCenter.default.post(
                name: Notification.Name.Task.DidComplete,
                object: strongSelf,
                userInfo: userInfo
            )

            strongSelf[task] = nil
        }

//下面是一系列判断后对上面那个闭包的调用,所以下面的不看了,我们分析上面的闭包

   ...一大堆判断后调用
            completeTask(session, task, error)
  ...

下发到具体的TaskDelegate的urlSession方法中:

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        if let taskDidCompleteWithError = taskDidCompleteWithError {
//是否自己实现了错误处理,是的话,传出去🍎
            taskDidCompleteWithError(session, task, error)
        } else {
            if let error = error {
//是否有错误,有的话进行错误处理🍎
                if self.error == nil { self.error = error }

                if
                    let downloadDelegate = self as? DownloadTaskDelegate,
                    let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
                {

                    downloadDelegate.resumeData = resumeData
                }
            }
//任务队列开启🍎
            queue.isSuspended = false
        }
    }

总结一下就是:

  • 创建URLRequest
  • 创建task任务,同时在结构体中保存了URLRequest
  • 通过URLSessiontask创建一个Request
  • Request进行resume
  • 到此.request解析完成,返回了request,形成链式请求.

未完待续,下一章节继续分析.

上一篇下一篇

猜你喜欢

热点阅读