Apple 文档 NSURLSession 翻译 - 管理下载内
NSURLSession
类及其相关类为下载内容提供了接口。这个 API 提供了一系列丰富的代理方法来支持授权,而且让你的 APP 在后台被挂起时也能继续下载。
通过代理服务器的和 SOCKS 网关为用户配置好系统设置,NSURLSession
类完全支持 data
, file
, ftp
, http
, https
类型的超链接。你也可以添加一些支持你自己定制的网络协议或超链接(专门为你自己的 APP 所用)
重大说明!
NSURLSession
的 API 有很多你在看文档时可能感觉并不重要,但实际上却以很复杂的方式来共同发挥作用的类。用这个 API 之前,你应该看一下 URL Session Programming Guide 来了解这些类之间相互调用的一些概念。
通过 NSURLSession
的 API,你的 APP可以创建一个或者多个事务,其中的每一个事务都用来协助一组相关的数据来传输任务。比如,如果你在写一个浏览器,你的 APP 可能在每一栏或是每一个窗口都创建了一个事务,可能一个用来进行交互,一个用来后台下载。在每一个事务中,你的 APP 都添加了一些了的任务,其中的每一个任务就代表一个请求或是一个特定的 URL(必要时进行重定向)
给定 URL事务的这些任务共享一个事务配置 session,这个对象定义好了连接行为,比如通往同一个主机的相似连接的最大连接数,不管是通过蜂窝移动网络还是别的方式来允许连接。session 的行为一部分是由创建这个配置对象时你调用的方法来决定:
-
单例 shared session (没有配置对象)用于普通请求。它像你自己创建的session一样可定制,但是如果你的请求有限的话它是一个很好的起点,通过调用 sharedSession 来获取这个 session。看这个方法的文档来得到它的一些限制信息。
-
Default sessions 的行为和 shared session 差不多(除非你自己进行了深度定制),但你必须要通过代理方法得到增量数据。调用 NSURLSessionConfiguration 中的 defaultSessionConfiguration 方法来创建 Default session。
-
Ephemeral sessions和Default sessions 很相似,但是不写入 caches,cookies, 或credentials。
调用 NSURLSessionConfiguration 中的 ephemeralSessionConfiguration 方法来创建 Ephemeral session。 -
Background sessions 可以在你的 APP 在后台被挂起时进行上传或下载。调用 NSURLSessionConfiguration 中的 backgroundSessionConfiguration: 方法来创建 background session。
session 配置对象还包含了对 URL cache 和 cookie 存储对象发起请求并处理相应的引用,具体取决于配置和请求类型。
当多种类型的事件发生时(比如授权失败时,数据传输到服务器时,数据准备好缓存时等等)session 中的任务也会共享一个通用的代理来让你提供和获取相关信息。对于所有的后台下载和上传,你必须提供一个遵从 NSURLSessionDownloadDelegate 的 OC 协议。否则,如果你不需要任何由代理提供的特性,在你创建 session 时就给 delegate 传 nil 来调用这个 API。
重大说明!
session 对你的 delegate 始终保持强引用,除非你的 app 退出或者 session已明确失效。如果你没有使 session 失效,你的 app 将会内存溢出,直到它终止。
在 session 中,你创建了可上传数据到服务器的任务,然后通过磁盘上的文件,或是内存中的一个或多个 NSData
对象来获取数据。 NSURLSession
的 API 提供了三种类型的任务:
-
data tasks 通过
NSData
对象来发送和接受数据。 data tasks 用于与服务器进行简短频繁的请求。 - Upload tasks 和 data tasks 类似,但是他们也可以发送数据(一般是通过文件的形式),也支持 app 后台上传。
- Download tasks 通过文件的形式来获取数据,并且支持 app 后台上传和下载。
和大多数网络接口一样,NSURLSession
是高度异步的。它通过两种方式中的其中一种来返回数据给 app,取决于你调用的方法:
- 当传输成功或是发生错误时调用 completion handler block;
- 当收到数据或是传输完成时调用 session 的代理方法。
除了通过代理来传递数据,如果你需要通过当前任务的状态(给出一些状态警示)做一些程序上的决定, NSURLSession
的 API 统一提供了状态和进度的属性,方便查询。
URL sessions 同样支持取消,重启,恢复,挂起任务,而且可以在他们停止的地方恢复挂起、取消、失败了的下载。
URL Session Class 层级结构
NSURLSession
API 由下面的类组成(下面的缩进显示的是子类的关系)
- NSURLSession — 一个 session 对象。
- NSURLSessionConfiguration — 用来初始化 session 的配置对象。
-
NSURLSessionTask — 在 session 中表示任务的基类。
- NSURLSessionDataTask — 用来获取 NSData 对象的 URL 的任务
- NSURLSessionUploadTask — 用来上传文件,并接收 NSData 对象的 URL 的任务。
- NSURLSessionDownloadTask — 以磁盘临时文件的形式获取 URL 的内容的任务。
- NSURLSessionStreamTask — 用来建立 TCP/IP连接的任务。
除此之外,NSURLSession
还提供了4个代理, 订了代理方法,你的 app 可以实现这些代理方法,提供对 session 和 task 更加细化的控制。
- NSURLSessionDelegate — 可处理 session level 事件的代理
- NSURLSessionTaskDelegate — 可处理和其他所有 task 类型相通的 task level 的方法的代理
- NSURLSessionDataDelegate — 可处理与数据和上传任务相关的 task level 的方法的代理
- NSURLSessionDownloadDelegate — 处理与下载任务相关的 task level 的方法的代理
- NSURLSessionStreamDelegate — 处理与数据流相关的 task level 的方法的代理
最终,NSURLSession
的 API 使用了很多类,这些类也同样使用了其他的 API,比如 NSURLConnection 和 NSURLDownload。共同使用的类包括:
- NSURL — 包含了 URL 的对象
- NSURLRequest — 封装与 URL 请求,包括 URL、 请求方法等等相关的元数据。
- NSURLResponse— 封装服务器相应的请求,如与 Content 的 MIME 类型和长度相关的元数据。
- NSHTTPURLResponse — 给 HTTP请求添加特定的附加元数据,比如 response headers。
-
NSCachedURLResponse — 用来缓存服务器响应的 body data 封装的一个
NSURLResponse
对象。
身份验证和 TLS 定制
当服务器要求身份验证或在 TLS 协商期间提供凭证时,URL Session 通过调用代理方法,让你可以自定义处理身份验证或证书验证。
调用的代理方法取决于你是在处理 task 相关还是 session范围内的挑战。Table 1 给出了两者之间的区别。
**Table 1 ** Session-level and connection-level challenges
对于 task 相关的挑战, session 会调用代理的
URLSession:task:didReceiveChallenge:completionHandler: 方法。
对于 session 相关的授权挑战,session 会调用代理的
URLSession:didReceiveChallenge:completionHandler: 方法,如果它不存在,则调用代理的URLSession:task:didReceiveChallenge:completionHandler: 方法。
当请求需要客户端授权时,如果你没有实现这些方法,URL Session 会通过下面的方式来尝试授权:
- 如果 URL中含有授权信息的话,通过请求的 URL 中的授权信息。
- 在用户的钥匙串( 在 OS X 中)或是 app 的钥匙串(在 iOS 中)寻找网络密码或是证书。
如果验证不可用,或是服务器拒绝了验证,那这个连接将会在没有授权的情况下继续。对于 HTTP 和 HTTPs 请求来说,连接会通过一个对应的 HTTP 状态码反应尝试失败, 也可能会提供可选择的内容(比如私有网站的公开版本)。对于其他类型的 URL(比如 FTP),将会直接连接失败。
注
erberos 身份验证的处理是公开透明的。这里描述的委托方法不适用于 Kerberos 身份验证。
App 传输安全 (ATS)
从 iOS9.0 和 OS X v10.11 开始,一种称为App 传输安全(ATS)的安全特性在所有用作 HTTP 连接的 NSURLSession 中是默认启用的。ATS 要求 HTTP 连接使用 HTTPS(RFC 2818)。
想了解更多信息,请看
Information Property List Key Reference 中的 NSAppTransportSecurity
使用 URL Session
使用 NSURLSession
类发起一个请求:
- 创建一个 session 配置。若是用作后台的 session,这个配置必须包括一个 唯一标识(unique identifier)。 存储这个标识, 当你的 app 崩溃或是被终止或是被挂起时用它来重连 session。
- 创建一个 session, 指定它的配置对象,或是代理。
- 在每个代表一个资源请求的 session 中创建 task 对象。这些 task 对象应该是
NSURLSessionTask—NSURLSessionDataTask, NSURLSessionUploadTask, or NSURLSessionDownloadTask 的子类,具体用哪个取决于你想要做什么。
每个task 开始时都处于挂起状态。你的 App 调用恢复任务后,它会开始下载其对应的资源。
在你开始一个 task 之后,session 会调用其代理方法,如下:
-
如果和服务器初始化握手需要一个连接 level 的挑战(比如SSL 的客户端证书),NSURLSession 会调用 URLSession:task:didReceiveChallenge:completionHandler: 或是 URLSession:didReceiveChallenge:completionHandler: 代理方法,如前面的
身份验证和 TLS 定制
所述。 了解更多为 NSURLSession 写一个授权代理方法的信息,请阅读 read URL Session Programming Guide. -
如果这个 task 的数据是由流提供的,NSURLSession 对象将会调用代理的 URLSession:task:needNewBodyStream: 代理方法来获取一个 提供新请求的 body data 的 NSInputStream 对象。
-
在初始化上传到服务器(如果可用的话)的 body 内容期间,代理会阶段性接收 URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend: 的回调来报告上传进度;
-
服务器发送一个响应;
-
如果响应表明需要授权,session 会调用代理方法
URLSession:didReceiveChallenge:completionHandler: ,回到步骤2; -
如果响应是一个 HTTP 重定向响应, NSURLSession 对象会调用代理方法
URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler: 代理方法会用提供的 NSRULRequest 对象(遵循重定向),或是新的 NSURLRequest 对象(如果需要重定向到一个不同的 URL 的话),或是 nil(将有效的响应作为重定向响应的 body,并将它作为结果返回) 来调用所提供的 completion handler 。
- 如果你决定追踪重定向,返回步骤2;
- 如果代理没有实现这个方法,这个重定向将会被重定向的最大数字跟进。
-
对于一个通过调用
downloadTaskWithResumeData:
或 downloadTaskWithResumeData:completionHandler: 方法创建的(重复)下载任务来说,NSURLSession 会对一个新的任务对象调用
URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes: 代理方法。 -
对于数据任务,NSURLSession 会调用
URLSession:dataTask:didReceiveResponse:completionHandler: 代理方法,来决定是否要将 数据任务转换成下载任务,然后调用完成回调来继续接收或下载数据。 如果你的 app 选择将数据任务转换成下载任务,NSURLSession 会将这个新的下载任务作为一个参数来调用 URLSession:dataTask:didBecomeDownloadTask: 代理方法。调用完之后,代理不会从数据任务接收到进一步回调,而是从下载任务那里来接收回调; -
在服务器传输过程中,代理会周期性地接收到一个 task-level 的回调方法来报告传输的进度。 对于数据任务,session 会在接收过程中通过真正的数据块来调用
URLSession:dataTask:didReceiveData: 代理方法。
对于下载任务来说,session 会通过已经成功写入磁盘的 bytes 来调用
URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite: 代理方法。如果用户告诉 app 来暂停下载,通过cancelByProducingResumeData: 然后,如果用户要求 app 继续下载任务,将返回的恢复数据给
downloadTaskWithResumeData:
或 downloadTaskWithResumeData:completionHandler: 方法来创建一个新的下载任务,来继续下载。(返回步骤1) -
对于数据任务,NSURLSession 对象可能会调用
URLSession:dataTask:willCacheResponse:completionHandler: 方法。你的 app 应该来决定是否允许缓存。如果你没有是现在合格方法,默认会使用 session 的配置对象中的缓存方案。 -
如果响应是由多个部分组成的编码,session 会在
didiRecieveReponse
代理被多次调用之后再次调用didRecieveData
代理方法。如果出现这样的情况,回调步骤8(处理didRecieveResponse
回调) -
如果下载任务成功完成, NSURLSession 对象会通过一个临时文件的地址来调用任务的 URLSessionDownloadTask:didFinishDownloadingToURL: 方法。你的 app 必须从这个文件中读取返回的数据,或是在这个代理方法 结束之前将它移到一个永久保存的地址。
-
任务完成时, NSURLSession 对象会通过一个错误对象或是
nil
(如果任务成功完成)来调用 URLSession:task:didCompleteWithError:
如果这个下载任务是可被重新唤起的,这个 NSError 对象的 userInfo 字典中会包含 key 为
NSURLSessionDownloadTaskResumeData 的值。你要将这个值传给
downloadTaskWithResumeData:
或是 downloadTaskWithResumeData:completionHandler: 来创建一个新的下载任务以继续这个已经存在的下载任务。
如果这个下载任务不能被重新唤起,你的 app 需要创建一个新的下载任务来从头开始下载。
不管是哪种情况,如果传输因任何服务器错误以外的原因而失败,返回到步骤3(创建或唤起任务对象)。
注
NSURLSession 不会通过 error 参数返回任何的服务器错误。通过 error 参数返回的错误只会是服务器端错误,比如不能解决 hostname,不能连接到 host。error codes 信息可在 URL Loading System Error Codes 中查询。
服务器端错误会通过 ** NSHTTPURLResponse** 对象中的HTTP 状态码来返回,了解更多信息,请阅读 NSHTTPURLREsponse 和 NSURLResponse 的文档。
- 如果你不需要用到 session 了,可以通过调用 invalidateAndCancel(用来取消未完成的任务)或是 finishTasksAndInvalidate (在这个对象失效前完成未完成的任务)。 如果你不使这个session失效,它会在你的app终止时自动消失(除非它是一个含有正在进行的任务的后台 session) session 失效后,当所有未完成的任务被取消或是完成时,session会调用 URLSession:didBecomeInvalidWithError: 方法。当代理方法返回时,session 会处置对代理的强引用。
如果你的app 取消了一个正在进行的下载任务, 当出现错误时,NSURLSession 对象会调用代理的 URLSession:task:didCompleteWithError: 方法。
后台处理注意事项
因为重启app(或是等待用户重新唤起时)代价是相当高的,在后台session中有些特性是不可用的。如下:
-
session 必须提供一个传递事件的代理,因为在传输进行中app可能会退出或是重启,完成事件的回调 Block 是不支持的、(为了上传和下载),这些代理在传输过程中表现是相同的;
-
只有 HTTP 和 HTTPS 协议是被支持的。其他内置的网络协议和用户网络协议都不被支持;
-
只有上传和下载是被支持的(没有 data 任务);
-
重定向一直被允许;
-
全系统同时进行的后台传输的数量是被限制的;
-
如果后台任务未能满足指定系统吞吐量限制,可能会被取消。也就是说,如果一个长期运行的任务在一段时间内没有发送或者接受足够的数据,它可能会被取消,以后再被唤起。所以,如果可能的话,让一个传输可被重新唤起是很重要的。
-
如果后台传输初始化时app是在后台,那这个任务将被当做可裁剪的。换言之,它将被当做 配置对象的 discretionary 属性为 true 的session中的一个任务。
-
译者注:也就是说如果你要传输大量的数据,最好设置这个属性为YES,这样的话系统会延迟传输,直到你手机接通电源或是连上 wifi。下载大数据什么的设置为YES就对了
如果这些限制和你 app 的需求有冲突,你可以在 non-background session 中将远程资源下载到一个文件中。这样,当你的用户让你的 iOS app 进入后台或是退出你的 OS X app 时,可以通过调用 cacelByProducingResumeData: 方法来暂停任何进行中的下载任务。当用户重新让 app 进入前台时恢复下载。如果你的 app 在你获取到任何恢复的数据之前终止了,就不能再恢复下载了。
注
后台 session 是为了优化传输少量很大的资源,在必要时可以进行续传。如果可以,你可能想要调查优化服务器端行为的方法 ,来实现这样的用法,比如:
- 在终结点发起发送或接收 zip 或 tar格式的压缩文件的请求,而不是分开调用多次;
- 在终结点发起发送或接收在服务器和客户端之间的增量差异的请求;
- 在终结点发起一个可返回上传ID的请求,这个ID可用来追踪和恢复传输到服务器的数据;
- 添加一个中间的web 服务器代理请求到规范的web服务器,以方便任何上述优化。
NSCopying 行为
session 和 task 对象都遵从 NSCopying 协议,如下:
- 当你的app 拷贝一个session 或是 task 对象时,你会得到一个同样的对象;
- 当你的app拷贝一个配置对象时,你会得到一个你可以独立地修改的拷贝对象。
线程安全
URL Session API 自身完全是线程安全的。你可以在任何一个线程上下文中随意创建 session 和 task,而且,当你的代理方法调用提供的 完成回调时,它的工作已自动被安排在正确的代理队列中。
警告
你的 URLSessionDidFinishEventsForBackgroundURLSession: 代理方法可能会在第二线程中被调用。但是,在iOS中,你完成那个方法时需要在
application:handleEventsForBackgroundURLSession:CompletionHandler: app 代理方法中调用completion handler。而且你必须在主线程中调用那个 completion handler。
创建 Session
+ sessionWithConfiguration:delegate:delegateQueue:
配置 Session
configuration Property
delegate Property
delegateQueue Property
sessionDescription Property
给 Session 添加数据任务
- dataTaskWithURL:completionHandler:
- dataTaskWithRequest:completionHandler:
给 Session 添加下载任务
- downloadTaskWithURL:completionHandler:
- downloadTaskWithRequest:completionHandler:
- downloadTaskWithResumeData:completionHandler:
给 Session 添加上传任务
- uploadTaskWithRequest:fromData:
- uploadTaskWithRequest:fromData:completionHandler:
- uploadTaskWithRequest:fromFile:
- uploadTaskWithRequest:fromFile:completionHandler:
- uploadTaskWithStreamedRequest:
给 Session添加流任务
- streamTaskWithHostName:port:- streamTaskWithNetService:
管理 Session
- getTasksWithCompletionHandler:
常量
NSURLSession-Specific NSError userInfo Dictionary Keys