iOS开发资料收集区iOS DeveloperiOS学习小集

苹果Network部分文档翻译第二章:URLSession

2017-07-14  本文已影响233人  董二千

负责协调一组相关网络数据传输任务的对象。

概论

URLSession和一系列相关的类,提供了一个API,来下载内容。这个API提供了一组丰富的代理方法来支持认证并给你的App一种在后台下载的能力,当你的App不在运行,或者在iOS系统中,你的App处于休眠状态的时候。

URLSession类支持data file ftp httphttps的url,支持代理服务和SOCKS通道。并被配置到用户的系统中

URLSession支持HTTP/1.1,SPDY,HTTP/2协议。HTTP/2的支持被描述在RFC 7540,并且需要一个服务支持,无论是ALPN还是NPN

你同样可以用URLProtocol为你自定义的网络协议和URL schemes提供支持

Important
URLSessionAPI通过一种灵活的方式调用许多类一起工作。如果你只看URLSesson的文档可能不能一下子就看出来。在使用这个API之前你应该阅读URL Session Programming Guide这篇文章,来获得一个总体的了解。为了弄清楚这些类是怎么互相合作的。

使用URLSession API,你的App可以创建一个或者更多的session,每个session负责协调一组数据传输任务。例如,如果你正在写一个网络部分,你的App可能要为每个tab或者window创建一个session,或者一个session用来交互,另一个用来进行后台的下载。在每个seesion中,你的App添加一些任务,每个任务代表了一个特定URL的请求。

每个被指定了URL的session的任务,共享一个session configuration object,这个object定义了网络链接的行为,例如,对于同一个host最大的并发链接数量;是否允许链接蜂窝网络等等。session的部分行为被确定,当你创建一个configuration object的时候:

session configuraion object同样包含一个URL cache和存储cookie对象的reference。在request和处理responses的时候可能会用到,这取决于configurationrequest type

在一个session中的很多任务共享公共的delegate。当不同的事件发生的时候,会让你获得一些消息。例如,认证失败、当从server获取的数据、数据准备被缓存等等。对于所有的后台下载和上传。你必须要提供一个代理者,遵循URLSessionDownloadDelegate
这个协议。如果你不需要delegate提供的消息,你可以在创建session的时候,delegate参数传入nil。

Important
session object强引用delegate,直到你的App退出,或者明确的让session失效。如果你不release session,你的App在退出前都存在一个内存泄露的问题。

通过session,你创建一个任务来上传数据到server,然后从server接受数据。无论是作为磁盘上的数据,还是在内存中一个或者更多的NSData对象。URLSession API提供了三种类型的任务:

除了向代理传输数据,URLSession API提供了status(状态)和progress(过程)的属性。你可以通过这些来判断,如果你需要基于任务当前状态做一个程序上的决定的时候。

URL session 同样支持canceling(取消)、restarting(重新开始)、resuming(开始或继续)、suspending(暂停)一个任务。并且提供一种继续已经暂停、取消、下载失败的任务的能力。

URL Session类的结构(URL Session Class Hierarchy)

NSURLSesson API用下面的类组成(展示了一些内嵌的父子类关系)

另外,NSURLSesson API 提供了五个协议。这些协议定义了你app能够实现的代理方法,提供了对于sesson和task的行为更加细粒度的控制。

最终,NSURLSession API使用大量的类,同时这些类也被其他的API使用例如NSURLConnection和NSURLDownload。这些类中的一些会被共享使用:

认证和自定义TLS(Authentication and TLS Customization)

当一个server请求认证或者在TLS认证提供证书的时候,URL session调用它代理的方法,允许你以一种自定义的方式处理认证或证书校验。这些方法的调用依赖是否你正在处理一个特殊的任务的变化或者一个session范围内的改变。下面的表格展示了这两者的不同:

Session-wide challenges Task-specific challenges
NSURLAuthenticationMethodNTLM NSURLAuthenticationMethodDefault
NSURLAuthenticationMethodNegotiate NSURLAuthenticationMethodHTTPBasic
NSURLAuthenticationMethodClientCertificate NSURLAuthenticationMethodHTTPDigest
NSURLAuthenticationMethodServerTrust

具体任务发生改变,session调用代理的urlSession(_:task:didReceive:completionHandler:)方法。

范围内的session认证发生改变,如果urlSession(_:didReceive:completionHandler:)方法被实现的话,session调用它,否则,调用代理的urlSession(_:task:didReceive:completionHandler:)方法。

如果你没有实现这些方法,当请求需要一个客户端认证的时候,URL session尝试如下方式进行认证:

Note
Kerberos认证是以一种透明的方式来处理的。这里描述的代理方法不适用于Kerberos 认证。

App传输安全(App Transport Security (ATS))

从iOS9.0和OS X v10.11开始,一个新的安全特性叫做App Transport Security(ATS)被启用。默认的所有的HTTP链接要用NSURLSession建立。ATS要求HTTP链接使用HTTPS(RFC 2818)。
更多信息,见在 Information Property List Key Reference中的 NSAppTransportSecurity

使用URL Session(Using an URL Session)

使用NSURLSession类建立请求:

  1. 创建一个session configuraton。对于background session,这个configuration必须包含一个唯一标志符。存储标志符,如果你的App crash或者被停止,或暂停,使用它来重新关联session。
  2. 创建一个session,指定一个configuration对象和一个可选的代理。
  3. 创建一个task object包含一个session。每个task代表了一个资源的请求。task object是 URLSessionTask的子类 —— URLSessionDataTask, URLSessionUploadTask, 或 URLSessionDownloadTask,这取决于你想要完成的行为。
    每个任务从一个初始的状态开始。在你的app调用task的resume()
    方法的时候,它开始下载具体的任务内容。

在开始一个任务之后,session调用它delegate上的如下方法:

  1. 如果和server的初次握手需要链接层的变化,例如:SSL客户端认证(SSL client certificate),NSURLSession调用urlSession(_:task:didReceive:completionHandler:)或者urlSession(_:didReceive:completionHandler:)这两个代理方法中的一种。正如上面在Authentication and TLS Customization说明的。
    更多关于NSURLSession的认证代理方法的信息请看URL Session Programming Guide
  2. 如果task的数据是从一个stream提供的,NSURLSession object调用代理的urlSession(_:task:needNewBodyStream:)这个方法,来获得NSInputStream object。它为新的request提供了数据body。
  3. 在初始化body内容上传到server的期间,代理周期性的接受urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)这个方法的callback。来监听数据的上传过程。
  4. server发送response
  5. 如果response指出认证是必须的,session调用它自己的代理方法urlSession(_:task:didReceive:completionHandler:)。回到第二步。
  6. 如果response是一个HTTP重定向response,NSSession对象调用它自己的代理方法urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)。这个方法会调用被提供的完成handler,通过一个被提供的NSURLRequest object(为了跟随重定向),一个新的NSURLRequest object(为了重定向到不同的URL),或者什么都不提供。
  1. 对于一个通过调用 downloadTask(withResumeData:)
    或者downloadTask(withResumeData:completionHandler:)
    方法创建的download task,NSURLSession调用代理的urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)通过使用新的task object。
  2. 对于data task,NSURLSesson object调用代理的urlSession(_:dataTask:didReceive:completionHandler:)方法。决定是否把data task转换成download task,接着,调用完成的callback继续接受数据或者下载数据。
    如果你的app选择把data task转换成download task,NSURLSession调用代理的 urlSession(_:dataTask:didBecome:)方法,通过传入新的download task作为参数。在调用之后,代理者不再接受来自data task的callback,并开始接受来自downlod task的回调。
  3. 在从server传输数据的时候,代理周期性的接受一个task层的callback来监听传输进度。
    对于一个data task,session调用代理的urlSession(_:dataTask:didReceive:)
    方法,得到接受到的实际数据。
    对于一个download task,session调用代理的urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)通过传入bytes的,成功的写到磁盘上。如果用户让你的app暂停下载,通过cancel(byProducingResumeData:) 方法取消任务。
    之后,如果用户要求你的app开始下载任务,传入被返回的重新开始的数据到方法 downloadTask(withResumeData:)或者方法downloadTask(withResumeData:completionHandler:)来创建一个新的下载任务来继续下载。(回到第一步)
  4. 对于一个data task,NSURLSession object可能调用代理的urlSession(_:dataTask:willCacheResponse:completionHandler:)方法。你的app要做出回应是否允许缓存。如果你没有实现这个方法,将会默认的使用在session configuration里面的缓存策略。
  5. 如果response是多部分编码,session可能再次调用代理的didReceiveResponse方法在调用更多次didReceiveData方法之后。如果这种情况发生了,回到第八步。

12.如果下载任务成功完成,接着,NSURLSession对象调用task的urlSession(_:downloadTask:didFinishDownloadingTo:)方法通过传入一个临时文件地址。你的App必须在代理方法return之前,从这个文件里读取response数据或者把数据移动到一个固定的地址。

13.Task完成的时候,NSURLSession调用代理的urlSession(_:task:didCompleteWithError:)方法,通过传入一个error object或者nil(如果任务成功完成)。如果download任务被重启,NSError对象的userInfo字典会包含一个以 NSURLSessionDownloadTaskResumeData为key的值。你的App需要传入这个值到方法 downloadTask(withResumeData:)
或者downloadTask(withResumeData:completionHandler:)
中来创建一个新的下载任务。来继续之前的下载。
如果任务不能被重启,你的App应该创建一个新的download task ,重新开始数据的传输。
无论哪种情况,如果你的传输因为一些原因失败了不是因为server的问题,回到第三步(创建并重新开始一个task)

Note
NSURLSession不能通过error parameter抛出server的error。唯一你代理能接收到的error是客户端这边的error。例如,不能解析主机域名,或链接到主机。错误码被描述在URL Loading System Error Codes中。
server的error code在NSHTTPURLResponse object中通过HTTP 的状态码(status code)的形式来抛出。更多的信息,请阅读HTTPURLResponseURLResponse的相关文档。

14.如果你不再需要session,你应该使它无效,通过调用invalidateAndCancel()或者finishTasksAndInvalidate()

在使session无效之后,当所有未结束的task被cancel或结束之后,session调用代理的urlSession(_:didBecomeInvalidWithError:)方法。当代理方法返回,session会release了它持有的代理的强引用。

如果你的App在下载过程中被cancel,NSURLSession object会调用代理方法urlSession(_:task:didCompleteWithError:),就相当于一个error发生了。

后台传输的顾虑(Background Transfer Considerations)

因为重启你的App(或者用户relaunch)的代价是非常昂贵的,一些特性是无法在后台的session里生效的。

如果以上要求和你App的需求冲突,你同样可以下载远程资源到文件中,用non-background的sesson。如果你这样做了,当用户把你的App退到后台或者退出macOS App,会通过cancel(byProducingResumeData:)暂停所有活动中的下载,当用户回到你的App,再继续下载。如果你的App在你获得resume data的时候就已经停止,你将不能继续之前的下载。

Note
Background session优化了较少数量的大量资源的传输,可以在需要的时候继续传输。如果可以,你应该尽可能的优化server的传输,避免这种情况的发生。

NSCopying行为(NSCopying Behavior)

Session和task object遵循NSCopying 协议,如下:

线程安全(Thread Safety)

URL Session API是线程安全的。你可以自由的创建session和task在任何线程中。当你的代理方法调用被提供的完成回调,它会自动的把调用分配到正确的队列里面。

Warning
你的 urlSessionDidFinishEvents(forBackgroundURLSession:)
session代理方法可以在一个辅助线程中被调用。但是,在iOS中,你的方法的实现可能需要调用在application(_:handleEventsForBackgroundURLSession:completionHandler:)方法里被你所提供的完成handler。你必须要在主线程里面调用该方法。


校验:LinkRober
本文译自URLSession
有翻译不准确的的地方或有待改进的地方欢迎指正🙏🙏🙏

上一篇下一篇

猜你喜欢

热点阅读