NSURLSession 在 2013年随着 iOS7 的发布一起面世的,苹果对它的定位是作为 NSURLConnection 的替代者,在 iOS9 之后苹果官方已经移除了 NSURLConnection,我们使用最广泛的第三方框架:AFNetworking、SDWebImage 的最新版也都已经全部使用了NSURLSession
与 NSURLConnection 相比,NSURLsession 最直接的改进就是可以配置每个 session
的缓存,协议,cookie
,以及证书策略(credential policy),甚至跨程序共享这些信息,这将允许程序和网络基础框架之间相互独立,不会发生干扰,每个 NSURLSession 对象都由一个 NSURLSessionConfiguration 对象来进行初始化,后者指定了刚才提到的那些策略以及一些用来增强移动设备上性能的新选项
NSURLSession 中另一大块就是 session task
。它负责处理数据的加载以及文件和数据在客户端与服务端之间的上传和下载。NSURLSessionTask 与 NSURLConnection 最大的相似之处在于它也负责数据的加载,最大的不同之处在于所有的 task
共享其创造者 NSURLSession 这一公共委托者(common delegate)
NSURLSession 指的也不仅是同名类 NSURLSession,还包括一系列相互关联的类,包括:NSURLSession、NSURLSessionConfiguration 以及 NSURLSessionTask 的 4 个子类:NSURLSessionDataTask,NSURLSessionUploadTask,NSURLSessionDownloadTask、NSURLSessionStreamTask(iOS 9 新增) ,还包括之前就存在的两个类;NSURLRequest 与 NSURLCache
接下来本文将讲解 NSURLSession 的使用
NSURLSession
NSURLSession 本身是不会进行请求的,而是通过创建 task 的形式进行网络请求(resume()
方法的调用),同一个 NSURLSession 可以创建多个 task,并且这些 task 之间的 cache 和 cookie 是共享的,NSURLSession 的使用有如下几步:
- 创建一个 NSURLSession 对象
- 使用 NSURLSession 对象创建一个 Task
- 启动执行 Task
在开始使用 NSURLSession 之前,有必要了解一下 NSURLSession 以及它的组成部分
NSURLSession 的创建
有两种方式创建 NSURLSession 对象:默认 NSURLSession 对象 和自定义 NSURLSession 对象
|
|
在使用自定义方式创建 NSURLSession 对象时,都需要传入一个NSURLSessionConfiguration 参数,NSURLSessionConfiguration 对以前 NSMutableURLRequest 所提供的网络请求层的设置选项进行了扩充,提供给我们相当大的灵活性和控制权。从指定可用网络,到 cookie,安全性,缓存策略,再到使用自定义协议,启动事件的设置,以及用于移动设备优化的几个新属性,你会发现使用 NSURLSessionConfiguration 可以找到几乎任何你想要进行配置的选项
NSURLSessionConfiguration
NSURLSession 在初始化时会把配置它的 NSURLSessionConfiguration 对象进行一次 copy,并保存到自己的 configuration 属性中,而且这个属性是只读的,因此之后再修改最初配置 session
的那个 configuration
对象对于 session
是没有影响的,也就是说,configuration
只在初始化时被读取一次,之后都是不会变化的
系统提供了创建一个 NSURLSessionConfiguration 实例对象的三种模式
default
:标准的 configuration,这个配置共享 Cookie,缓存和证书ephemeral
:临时 configuration,与默认配置相比,这个配置不会将缓存、Cookie等存在本地,只会存在内存里,所以当程序退出时,所有的数据都会消失background
:后台 configuration 配置,该配置在后台完成上传和下载,在创建 Configuration 对象的时候需要提供一个identifier 用于标识完成工作的后台会话
|
|
创建了 NSURLSessionConfiguration 对象就可以给它设置各种属性
|
|
URLSessionTask
NSURLsessionTask 是一个抽象类,其下有四个实体子类可以直接使用:NSURLSessionDataTask、NSURLSessionUploadTask、NSURLSessionDownloadTask、NSURLSessionStreamTask,这四个子类封装了现代程序四个最基本的网络任务:获取数据,比如JSON或者XML,上传文件和下载文件以及数据流的获取,这四个类有如下关系
NSURLSessionDataTask
NSURLSessionDataTask 是开发中使用频率最多的,我们平常使用的 GET 和 POST 请求都是通过它来实现的,NSURLSessionDataTask 可以通过 NSURL 或 NSURLRequest 创建(使用前者相当于是使用一个对于该 URL 进行标准 GET 请求),如果请求的数据简单并且不需要对获取的数据进行复杂操作,我们使用 Block 处理返回的数据
普通 GET 请求
|
|
普通 POST 请求
|
|
另外,我们也可以设置 session 的代理监听网络请求变化,具体的协议为NSURLSessionDelegate,它有四个直接或间接子协议分别是 NSURLSessionTaskDelegate、NSURLSessionDataDelegate、NSURLSessionDownloadDelegate 和
NSURLSessionStreamDelegate,它们关系如下:
|
|
NSURLSessionDownloadTask
NSURLSessionDownloadTask 主要用于下载,有两种方式:Block 和 代理,NSURLSession 在下载文件的时候,是将数据一点点地写入本地的一个临时文件,这个临时文件系统会很很快删除,所以我们需要把文件从这个临时地址移动到一个永久的地址保存起来,这样才算完整的下载完一个文件,另外,使用 NSURLSessionConfiguration 的 background 模式可以做到后台下载,并且即使应用被 Kill 之后也还可以恢复之前的下载任务
Block 方式下载文件
使用 Block 方式适合下载小文件,并且不需要监听下载进度,并且文件下载完成才会调用 Block
|
|
代理方式下载文件
使用代理方式适合下载大文件,并且可以随时监听文件的下载进度、暂停文件下载等
|
|
断点续传下载文件
使用 NSURLSession 下载任务支持断点续传,并且使用起来比 NSURLConnection 更加简单,下面看一下如何使用 NSURLSession 断点下载文件
|
|
任务的暂停除了使用 cancel(byProducingResumeData completionHandler:
之外,还可以使用 suspend()
,但是如果使用 suspend()
暂停任务,因为任务时可恢复的,那么对应的下载任务对象也是唯一的,也就是说 suspend()
和 resume()
需要成对使用,都是同一个 NSURLSessionDownloadTask 调用
|
|
这里需要注意的是 suspend()
暂停任务过久会超时,超时时间取决于 NSURLRequest
设置的超时时间,系统默认是 60 秒
后台下载文件
之前我们提到 NSURLSession 使得网络构架和应用程序可以独立工作、互不干扰,这就可以做到添加到后台的 NSURLSessionTask 任务在外部进程运行,即使应用程序被挂起,崩溃,或者被杀死,它依然可以运行,也就是说通过 NSURLSession 可以实现真正的后台下载
创建后台下载的操作步骤:
- 创建后台下载用的 NSURLSession 对象,configuration 设置 background 类型
- 用这个 NSURLSession 对象生成 NSURLSessionDownloadTask,并开始下载
- 在 AppDelegate 里实现
handleEventsForBackgroundURLSession
方法 - 实现 NSURLSessionDownloadDelegate 中必要的代理方法
看具体代码实现
AppDelegate 部分代码
|
|
ViewController 代码
|
|
在下载过程中,如果按 App 被切换到后台,Session 的Delegate 不会再收到任务的消息,此时系统会接管任务继续下载,直到所有的任务都下载完成之后,系统会调用AppDelegate的application:handleEventsForBackgroundURLSession:completionHandler:
回调方法
,这个方法有一个block类型的参数 completionHandler,它的意义在于,一旦它被执行就表明后台任务处理完成,因此 App 被唤醒之后就会一直等待 completionHandler的执行,以便让App重新停止运行,因此,要记得把completionHandler传给 Session 的代理,并且在 Session 的代理方法 URLSessionDidFinishEventsForBackgroundURLSession:
中调用它,然后这样就实现了在最后一个任务处理完毕以后App再次进入后台
如果 App 被切换到后台,在任务没有完成之前就被系统杀死了,APP 重新启动后怎么继续恢复之前的下载呢?iOS 系统在APP 被杀掉前会保存应用下载的 Session 的信息,在应用重新启动之后,用之前相同的 identifier 来新建一个 NSURLSessionConfiguration对象,并且用这个 NSURLSessionConfiguration 对象新建一个 Session,这个 Session 就可以当成是原来的 Session 了,这样创建的 Session,正在运行的任务就会自动和它关联起来,一旦这个 Session 被创建并且设置了 Delegate,iOS 系统会立即对之前的下载任务来到 URLSession:task:didCompleteWithError:
这个代理方法,之后可以在这个代理方法里使用 resumeData 恢复下载任务了
后台传输注意事项:首先,后台传输只会通过Wi-Fi来进行,后台下载的时间与以前的关闭应用后X分钟的模式不一样,而是为了节省电力变为离散式的下载,并与其他后台任务并发(比如接收邮件等),后台任务只支持http和https协议,不支持自定义协议,如果后台传输的任务是在 App 已经进入了后台之后才初始化的,那么 Session 的 NSURLSessionConfiguration 对象的 discretionary 属性值设置为 true
NSURLSessionUploadTask
使用 NSURLSession 上传和 NSURLConnection 差不多,都是把需要上传的数据以表单的形式拼接在请求体中,NSURLSession 中使用NSURLSessionUploadTask 上传文件,创建NSURLSessionUploadTask 有以下两种方式
|
|
表单拼接格式是固定的,必须严格按照规定的格式设置,具体格式如下
|
|
下面是的具体代码
|
|
上面是上传图片的代码,实际开发中我们可能需要上传其他类型的文件,这个时候需要获取当前需要上传的文件的 MIME Type
,并且上传文件的时候还有别的参数,上面的代码修改一下增加一个获取 MIME Type
的方法,并且增加拼接普通参数到请求体中
|
|
URLSessionStreamTask
URLSessionStreamTask 提供了一个通过URLSession创建的TCP / IP连接的接口,这个类是iOS 9 之后提供的,本人没有使用过,网上关于NSURLSessionStreamTask 的资料很少,就不班门弄斧了,以后有机会再补充吧
总结
以上就是关于 NSURLSession 的基本用法,具体的使用根据需要自己去实现,在实际开发中,我们都会使用第三方网络库AFNetworking(OC)或者 Alamofire(Swift)进行网络请求,虽然很少使单独使用 NSURLSession,但是这些优秀的网络库都是以 NSURLSession 为基础的,所谓万变不离其宗,不仅要会使用第三方库,基础的东西也需要明白,有时间再总结一篇AFNetworking 的使用