Alamofire的Request解析

女神镇楼

上一篇我们主要说了一下Alamofire中对一个普通的DataRequest的响应序列化的步骤。这次我们针对Requset的一个过程简要分析下

首先我们看下一个普通的请求的流程经过了哪些步骤

之前分析过Alamofire中的静态方法都是调用SessionManager里面的方法,SessionManager里的default存放着默认的session,而SessionDelegate则实现了session的代理。

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

再来看看SessionManager里面

首先声明了许多闭包,如果你想自己定义你的接收响应的逻辑你可以实现这些闭包

// MARK: URLSessionDelegate Overrides

/// Overrides default behavior for URLSessionDelegate method `urlSession(_:didBecomeInvalidWithError:)`.
open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)?

/// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`.
open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?

再来看看里面代理方法的实现

    /// Tells the delegate that the data task has received some of the expected data.
///
/// - parameter session:  The session containing the data task that provided data.
/// - parameter dataTask: The data task that provided data.
/// - parameter data:     A data object containing the transferred data.
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 {
    
    ///否则通过保存的[task:request]来获取到request,调用其taskDelegate实例对象的方法,将数据传递给了TaskDelegate
        delegate.urlSession(session, dataTask: dataTask, didReceive: data)
    }
}

可以看到将数据传递到了TaskDelegate里,再来看下TaskDelegate是怎么处理的

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 {
        ///如果实现了request的stream方法,则这里将数据通过闭包传递
            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) }
        }
    }
}

我们可以看到,针对一个普通的Data请求的话,这里的data就保存了返回的响应数据。

override var data: Data? {
    if dataStream != nil {
        return nil
    } else {
        return mutableData
    }
}

所以后面在序列化返回数据的时候我们可以看到

这里的data就是获取的request.delegate.data
var dataResponse = DataResponse<T.SerializedObject>(
            request: self.request,
            response: self.response,
            data: self.delegate.data,
            result: result,
            timeline: self.timeline
        )

总的来说,一次普通的请求流程是

  • 获取URLRequest
  • 获取URLSessionTask
  • SessionDelegate接受到代理回调传递给TaskDelegate,TaskDelegate进行处理并保存在自己的属性里

分步骤详细解析

我们还是从最基础的DataRequest的一个方法来逐行进行分析

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

这里通过传递一个URLRequestConvertible类型的参数来开始一个请求

  • 通过URLRequestConvertible获取URLRequest

URLRequestConvertible是一个协议,只有一个asURLRequest的方法,所以我们知道,你可以传任意类型哪怕是一个PigClass类,只要实现这个协议方法能够返回URLRequest即可,从这里我们可以看到但凡是后缀是Convertible的协议都是类似于这样的便利方法,就像我们前面分析过的URLConvertible一样

  • 通过TaskConvertible获取到URLSessionTask且对urlRequest进行自定义加工
    1. 通过TaskConvertible获取到URLSessionTask

         let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
      

      这里的originalTask类型其实是DataRequest的结构体Requestable的一个实例初始化需要传入urlRequest,而这个结构体遵守了协议TaskConvertible,所以我们知道这里可以获取到URLSessionTask

    2. urlRequest进行自定义加工

      在协议TaskConvertible的方法声明我们看到传递了一个 RequestAdapter 的参数,且在结构体Requestable的协议实现内我们可以看到这行

         let urlRequest = try self.urlRequest.adapt(using: adapter)
      

      点击这里self.urlRequestadapt方法

         func adapt(using adapter: RequestAdapter?) throws -> URLRequest             {
             guard let adapter = adapter else { return self }
             return try adapter.adapt(self)
         }
      

      是不是绕晕了???哈哈,只能说作者封装的太仔细了

      其实这里的RequestAdapter协议就是专门对URLRequest加工的一个协议,从它的协议声明方法可以看出。

    3. 返回URLSessionTask

      许多人可能对最后的返回觉得很奇怪

       return queue.sync { session.dataTask(with: urlRequest) }
      

      这里返回的是URLSessionTask么??点击这个sync方法你就可以看到了

       public func sync<T>(execute work: () throws -> T) rethrows -> T
      

      最后返回的是一个泛型函数,而这个泛型函数返回的结果就是函数参数() throws -> T返回的值,而session.dataTask(with: urlRequest)返回的是URLSessionTask

  • request进行包装

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

    这里调用的是DataRequest父类Requestinit方法,其第三个参数requestTask是一个枚举类型,用来对request进行分类并关联值的,在这个init方法里面我们可以看到

      case .data(let originalTask, let task):
          taskDelegate = DataTaskDelegate(task: task)
          self.originalTask = originalTask
    

    这里用我们之前获取到的sessionTask来初始化DataTaskDelegate,而之前我们说了TaskDelegate以及其子类是我们最终接收Session响应的地方。

  • 一个request请求的开始、暂停、结束

    通过上面我们能看到初始化了一个DataTaskDelegate对象,我们看初始化方法

      init(task: URLSessionTask?) {
      _task = task
    
          self.queue = {
              let operationQueue = OperationQueue()
    
              operationQueue.maxConcurrentOperationCount = 1
              operationQueue.isSuspended = true
              operationQueue.qualityOfService = .utility
    
              return operationQueue
          }()
      }
    

    一个request对应着一个TaskDelegate对象,我们再来看Request中的resume()suspend()cancel()方法本质上都是控制taskDelegatetask对象。

  • TaskDelegatequeue

    前面我们可以看到通过Request获得URLSessionTask,然后执行任务。而这些任务是在SessionManager的一个queue这个队列里同步进行的。这个queue是初始化SessionManager时候用uuid生成的一个不会重复的队列

      let queue = DispatchQueue(label: "org.alamofire.session-manager." + UUID().uuidString)
    

    所以所有的网络请求,如果我们用的是同一个session的话,那么这些请求虽然是在后台执行的,但是是同步的。所以有时同时处理多个请求时,我们可以采用多个session

    TaskDelegate里也有一个queue

      /// The serial operation queue used to execute all operations after the task completes.
      open let queue: OperationQueue
    

    这是OperationQueue类型(不熟悉的可以先了解下),从注释来看这是当任务执行完成后才会开始执行任务的一条队列。

    在此文件的didCompleteWithError方法里我们可以看到queue.isSuspended = false,所以这是当URLSessionTask完成之后,此queue才开始执行,且此队列添加的第一个任务就是在Request的初始化方法里的

      delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
    

    结合之前在didCompleteWithError启动队列,所以第一个任务就是获取该请求的结束时间,后面的响应处理在前面的一篇文章中可以看到。

单纯的技术分析,也没啥图。如果这都能从头看到尾的话,我佩服你。哈哈。

这次简单通过一个DataRequest来分析了下请求的一个过程,后面还会写一篇文章来分析下别的类型的Request的设计思路和我对于Alamofire的一篇最终总结吧。希望能学习到作者的代码设计风格。

最终

太轻易得到的往往都不会去珍惜,真正失去了却又后悔莫及。

联系方式

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容