Alamofire(二)URLSession

@TOC

  • 最近在学习Alamofire框架,上一篇博客:Alamofire学习(一)网络基础 讲解了一些网络协议相关的知识,本篇博客主要是讲解一下Alamofire框架用到的IOS系统API URLSession.
  • 为什么要先学习URLSession

Alamofire中是使用URLSession进行封装的,所以有必要去先深入了解下URLSession. URLSession等同于NSURLSession,只是前者是Swift中的名字,后者是OC中的名字,他们之间是可以直接相互转换的。NSURLSession是同iOS7一同推出的,主要是对NSURLConnection进行了重构和优化,并且NSURLConnection在iOS 9的时候也已经被废弃,所以NSURLSession是NSURLConnection的取代者。

URLSession简介

Alamofire是一个为iOS和macOS打造的并基于Swift的网络库.它在Apple的基础网络架构上提供了更加优雅的接口来简化繁重而常用的网络请求任务。
Alamofire提供了链式的request/response方法,JSON的传参和响应序列化,身份认证和其他特性。Alamofire的优雅之处在于它完完全全是由Swift写成的,并且没有从它的Objective-C版本-AFNetworking那继承任何特性。

因为我们的Alamofire是对苹果URLSession的封装的网络框架,所以在探索Alamofire之前,我们除了需要掌握一些必备的网络相关知识外,还需要熟悉IOS的系统API URLSession

先来看一张概要图:

URLSession-kyl

URLSession类成员属性

1. URLSessionConfiguration

  • 3种会话模式
会话模式 特点 描述
默认会话模式(default) 工作模式类似于原来的NSURLConnection,使用的是基于磁盘缓存的持久化策略,使用用户keychain中保存的证书进行认证授权。 默认模式,通常我们用这种模式就足够了。default模式下系统会创建一个持久化的缓存并在用户的钥匙串中存储证书
瞬时会话模式(ephemeral) 该模式不使用磁盘保存任何数据。所有和会话相关的caches,证书,cookies等都被保存在RAM中,因此当程序使会话无效,这些缓存的数据就会被自动清空。 系统没有任何持久性存储,所有内容的生命周期都与session相同,当session无效时,所有内容自动释放。
后台会话模式(background) 该模式在后台完成上传和下载,在创建Configuration对象的时候需要提供一个NSString类型的ID用于标识完成工作的后台会话。 background创建一个可以在后台甚至APP已经关闭的时候仍然在传输数据的会话。background模式与default模式非常相似,不过background模式会用一个独立线程来进行数据传输。background模式可以在程序挂起,退出,崩溃的情况下运行task。也可以利用标识符来恢复进。注意,后台Session一定要在创建的时候赋予一个唯一的identifier,这样在APP下次运行的时候,能够根据identifier来进行相关的区分。如果用户关闭了APP,IOS 系统会关闭所有的background Session。而且,被用户强制关闭了以后,IOS系统不会主动唤醒APP,只有用户下次启动了APP,数据传输才会继续

2. URLSessionTask

2. 1 URLSessionDataTask:数据

URLSessionDataTask: 处理从HTTP get请求中从服务器获取数据到内存中。

Use URLSession’s dataTask(with:) and related methods to create URLSessionDataTask instances. Data tasks request a resource, returning the server’s response as one or more NSData objects in memory. They are supported in default, ephemeral, and shared sessions, but are not supported in background sessions

2. 2 URLSessionUploadTask:上传

URLSessionUploadTask:上传硬盘中的文件到服务器,一般是HTTP POST 或 PUT方式

Use URLSession’s uploadTask(with:from:) and related methods to create URLSessionUploadTask instances. Upload tasks are like data tasks, except that they make it easier to provide a request body so you can upload data before retrieving the server’s response. Additionally, upload tasks are supported in background sessions.

2. 3 URLSessionDownloadTask:下载

URLSessionDownloadTask: 从远程服务器下载文件到临时文件位置

Use URLSession’s downloadTask(with:) and related methods to create URLSessionDownloadTask instances. Download tasks download a resource directly to a file on disk. Download tasks are supported in any type of session.

2. 4 URLSessionStreamTask:流

Use URLSession’s streamTask(withHostName:port:) or streamTask(with:) to create URLSessionStreamTask instances. Stream tasks establish a TCP/IP connection from a host name and port or a net service object.

3. URLSessionDelegate

4. 缓存策略 NSURLRequestCachePolicy

缓存策略 作用 说明
NSURLRequestUseProtocolCachePolicy = 0 默认缓存策略 如果一个NSCachedURLResponse对于请求并不存在,数据将会从源端获取。如果请求拥有一个缓存的响应,那么URL加载系统会检查这个响应来决定,如果它指定内容必须重新生效的话。假如内容必须重新生效,将建立一个连向源端的连接来查看内容是否发生变化。假如内容没有变化,那么响应就从本地缓存返回数据。如果内容变化了,那么数据将从源端获取
NSURLRequestReloadIgnoringLocalCacheData = 1 URL应该加载源端数据,不使用本地缓存数据
NSURLRequestReloadIgnoringLocalAndRemoteCacheData =4 本地缓存数据、代理和其他中介都要忽视他们的缓存,直接加载源数据
NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData
NSURLRequestReturnCacheDataElseLoad = 2 指定已存的缓存数据应该用来响应请求,不管它的生命时长和过期时间。如果在缓存中没有已存数据来响应请求的话,数据从源端加载
NSURLRequestReturnCacheDataDontLoad = 3 指定已存的缓存数据用来满足请求,不管生命时长和过期时间。如果在缓存中没有已存数据来响应URL加载请求的话,不去尝试从源段加载数据,此时认为加载请求失败。这个常量指定了一个类似于离线模式的行为
NSURLRequestReloadRevalidatingCacheData = 5 指定如果已存的缓存数据被提供它的源段确认为有效则允许使用缓存数据响应请求,否则从源段加载数据。

5. URLSession 属性

属性类别 属性名称 作用
常规
常规 identifier 配置对象的后台会话标识符
常规 httpAdditionalHeaders 与请求一起发送的附加头文件的字典
常规 networkServiceType 网络服务的类型
常规 allowsCellularAccess 一个布尔值,用于确定是否应通过蜂窝网络进行连接
常规 timeoutIntervalForRequest 等待其他数据时使用的超时间隔
常规 timeoutIntervalForResource 资源请求应该允许的最大时间量
常规 sharedContainerIdentifier 应该下载后台URL会话中的文件的共享容器的标识符
常规 waitsForConnectivity 一个布尔值,指示会话是否应等待连接变为可用或者立即失败
设置Cookie政策
httpCookieAcceptPolicy 决定何时应该接受Cookie的策略常量
httpShouldSetCookies 一个布尔值,用于确定请求是否应包含来自Cookie存储的Cookie
httpCookieStorage 管理cookie存储的单一对象(共享实例)
HTTPCookie 表示HTTP cookie的对象。它是一个不可变的对象,从包含cookie属性的字典中初始化
设置安全策略
tlsMaximumSupportedProtocol 在此会话中进行连接时客户端应请求的最大TLS协议版本
tlsMinimumSupportedProtocol 协议协商期间应该接受的最小TLS协议
urlCredentialStorage 提供身份验证凭据的凭证存储
设置缓存策略
urlCache 用于向会话中的请求提供缓存响应的URL缓存
requestCachePolicy 一个预定义常量,用于确定何时从缓存中返回响应
支持后台转移
sessionSendsLaunchEvents 一个布尔值,指示在传输完成时是否应该在后台继续或启动应用程序
isDiscretionary 一个布尔值,用于确定是否可以根据系统的判断来调度后台任务以获得最佳性能
支持自定义协议
protocolClasses 在会话中处理请求的额外协议子类的数组
URLProtocol 一个NSURLProtocol对象处理加载协议特定的URL数据。在NSURLProtocol类本身是一个抽象类,可以为与特定URL方案的URL处理基础设施。您可以为您的应用支持的任何自定义协议或URL方案创建子类
支持多路径TCP
multipathServiceType 指定用于通过Wi-Fi和蜂窝接口传输数据的多路径TCP连接策略的服务类型
URLSessionConfiguration.MultipathServiceType 指定多路径TCP使用的服务类型的常量
设置HTTP策略和代理属性
httpMaximumConnectionsPerHost 同时连接到给定主机的最大数量
httpShouldUsePipelining 一个布尔值,用于确定会话是否应使用HTTP流水线
connectionProxyDictionary 包含有关在此会话中使用的代理信息的字典
支持连接变化
waitsForConnectivity 一个布尔值,指示会话是否应等待连接变为可用或者立即失败
属性类别 属性名称 作用

URLSession使用

URLSession使用流程

URLSession使用流程

URLSession上传

详情可以参考:苹果官方文档:Uploading Streams of Data

URLSession上传

func postsesssionUploadTask(){
        //1.创建会话对象
        let config:URLSessionConfiguration=URLSessionConfiguration.default
        let session:URLSession=URLSession.init(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
        //2.根据会话对象创建task
         let urlstr="\(BASEURL)"+LUNBOURL
        let urls: NSURL = NSURL(string: urlstr)!
        //3.创建可变的请求对象
        var request:URLRequest  = URLRequest(url: urls as URL)
        //4.修改请求方法为POST
        request.httpMethod = "POST"
        //5.设置请求体-----可以不设置,有默认的
        request.httpBody = "".data(using: String.Encoding.utf8)
        //6.根据会话对象创建一个Task(发送请求)
        /*
         第一个参数:请求对象
         第二个参数:completionHandler回调(请求完成【成功|失败】
         data:响应体信息(期望的数据)
         response:响应头信息,主要是对服务器端的描述
         error:错误信息,如果请求失败,则error有值
         upDta:要上传的二进制数据
         */
let images:UIImage=UIImage.init(named: "bannerhomeOne")!
        let upData:Data=UIImagePNGRepresentation(images)!
//通过data数据上传
        let upTask=session.uploadTask(with: request, from: upData) { (data, res, error) in
            //上传完毕后
            if error != nil{
                print(error)
            }else{
                let str = String(data: data!, encoding: String.Encoding.utf8)
                print("上传完毕:\(str)")
            }
        }
       upTask.resume()
}    

URLSession下载

普通下载

  • (1)先开启一个下载task
let configuration = URLSessionConfiguration.background(withIdentifier: self.createID())

        let session = URLSession.init(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
        
        session.downloadTask(with: url).resume()
  • (2)设置代理,接受回调,保存文件
//下载完成之后就回调URLSessionDownloadDelegate代理
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        print("下载完成 - \(location)")
    }
    
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        print(" bytesWritten \(bytesWritten)\n totalBytesWritten \(totalBytesWritten)\n totalBytesExpectedToWrite \(totalBytesExpectedToWrite)")
        print("下载进度: \(Double(totalBytesWritten)/Double(totalBytesExpectedToWrite))\n")
    }

后台下载

后台下载除了需要普通下载的必须步骤(1,2)外还需做如下处理

  • (3)开启后台下载权限
 //用于保存后台下载的completionHandler
  var backgroundSessionCompletionHandler: (() -> Void)?
  
  func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
      self.backgroundSessionCompletionHandler = completionHandler
  }
  • (4)下载完成时,调用系统回调,更新屏幕
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
      print("后台任务下载回来")
      DispatchQueue.main.async {
          guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let backgroundHandle = appDelegate.backgroundSessionCompletionHandler else { return }
          backgroundHandle()
      }
  }

断点续传下载

  1. 定义好变量,并遵循NSURLSessionDownloadDelegate协议
    var downloadTask : NSURLSessionDownloadTask?
    var partialData : NSData?
    var session : NSURLSession?
    var request : NSMutableURLRequest?
  1. 为每个按钮增加点击事件
    //开始下载
    @IBAction func onDownLoad(sender: AnyObject) {
        self.downloadFile()
    }
    //挂起下载
    @IBAction func onSuspend(sender: AnyObject) {
        if(self.downloadTask != nil)
        {
            //挂起下载任务,将下载好的数据进行保存
            self.downloadTask?.cancelByProducingResumeData({ (resumeData:NSData!) -> Void in
                self.partialData = resumeData
                self.downloadTask = nil
            })
        }
//        downloadTask!.suspend()
    }
    //恢复下载
    @IBAction func onResume(sender: AnyObject) {
        if(self.downloadTask == nil)
        {
            //判断是否又已下载数据,有的话就断点续传,没有就完全重新下载
        if(self.partialData != nil)
        {
            self.downloadTask = self.session?.downloadTaskWithResumeData(self.partialData!)
            }
        else{
            self.downloadTask = self.session?.downloadTaskWithRequest(self.request!)
            }
        }
        downloadTask!.resume()
    }
    //开始下载文件
    func downloadFile()
    {
        NSLog("正在下载")
        //创建URL
        var urlStr:NSString = NSString(string: "http://cdn.wall88.com/51a317b5ef36713194.jpg")
        urlStr = urlStr.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
        var url = NSURL(string: urlStr)!
        //创建请求
        request = NSMutableURLRequest(URL: url)
        //创建默认会话
        var sessionConfig : NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
        sessionConfig.timeoutIntervalForRequest = 20 //设置请求超时时间
        sessionConfig.allowsCellularAccess = true //是否允许蜂窝网络下载
        //创建会话
        session = NSURLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)//指定配置和代理
        downloadTask = session!.downloadTaskWithRequest(request!)
        downloadTask!.resume()
    }

  1. 根据下载内容的大小更新进度条
    //设置页面状态
    func setUIStatus(totalBytesWritten : Int64,expectedToWrite totalBytesExpectedToWrite:Int64 )
    {
        //调用主线程刷新UI
        dispatch_async(dispatch_get_main_queue(), {
            if(Int(totalBytesExpectedToWrite) != 0 && Int(totalBytesWritten) != 0)
            {
                //更新进度条
                self.ps.progress = Float(Float(totalBytesWritten) / Float(totalBytesExpectedToWrite))
                if(totalBytesExpectedToWrite == totalBytesWritten)
                {
                    self.lbl_hint.text! = "下载完毕"
                    UIApplication.sharedApplication().networkActivityIndicatorVisible = false
                    self.btn_download.enabled = true
                }
                else{
                    self.lbl_hint.text = "正在下载"
                    UIApplication.sharedApplication().networkActivityIndicatorVisible = true
                }
            }
            }
        )
    }
  1. 下载文件
    //任务完成,不管是否下载成功
    func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
        self.setUIStatus(0, expectedToWrite: 0)
        if(error != nil)
        {
            NSLog("error is:\(error!.localizedDescription)")
        }
    }
    
    //下载完成
    func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
        var error:NSError?
        var cachePath : NSString = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomainMask.UserDomainMask, true).first as NSString
        var savePath = cachePath.stringByAppendingPathComponent(lbl_title.text!)
        NSLog("\(savePath)")
        var saveUrl : NSURL = NSURL(fileURLWithPath: savePath)!
        var defalutManager = NSFileManager.defaultManager()
        //判断文件是否存在,存在则删除
        if(defalutManager.fileExistsAtPath(savePath))
        {
            defalutManager.removeItemAtPath(savePath, error: &error)
        }
        //下载成功后,文件是保存在一个临时的目录中的,需要自己拷置到该文件的目录
        defalutManager.copyItemAtURL(location, toURL: saveUrl, error: &error)
        if(error != nil)
        {
        NSLog("\(error)")
        }
    }
    
    //下载中
    func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        self.setUIStatus(totalBytesWritten, expectedToWrite: totalBytesExpectedToWrite)
    }

参考大神博客:https://juejin.im/post/5d544099e51d4561df780588

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

推荐阅读更多精彩内容

  • 孔子世家赞作者:司马迁太史公曰:《诗》有之:“高山仰止,景行行止。”虽不能至,然心乡往之。余读孔氏书,想见其为人。...
    zwb_jianshu阅读 216评论 0 0
  • 一上午特长,下午一点多我说你做个试卷,我让弟弟睡觉,一下我也睡着了,一个小时起来一看,做完第三单元的正面,...
    小星xing星阅读 97评论 0 0
  • 问题: /usr/local/lib/python3.5/dist-packages/h5py/__init__....
    Brlat阅读 379评论 0 0