一.概述
NSURLSession始于ios7.它具有访问接口,上传/下载数据,断点继传和后台下载等功能:其使用步骤:
1.创建session指定其configuration
2.由session执行任务得到task
3. task调用resume,启动网络请求
1.task分类
session的任务有四种:
1.数据任务Data task
2.下载任务Download task
3.上传任务Upload task
4.流任务Stream task ios9之后出现的,用于TCP/IP流
2. configuration类型
configuration的类型有三种:
1.默认配置Defaultsessions:
使用磁盘缓存,用将证书存在用户的钥匙串
2.即时配置Ephemeralsessions:
不使用磁盘缓存,也存储证书,它的信息存于RAM中,如果session被invalidate,这些信息也被清理掉
3.后台配置Background sessions:
配置上同默认配置,但是有一个独立进程来操作上传/下载
3.session生成task方式
对于生成每种task的方法,共有4种方式,举downloadTask为例子
1.用urlrequest请求:
public func downloadTaskWithRequest(request: NSURLRequest)->
NSURLSessionDownloadTask
2.用url请求:
public func downloadTaskWithURL(url: NSURL)-> NSURLSessionDownloadTask
3.带handler的URLRequest请求,注意,如果写了handler,就不会进入代理方法,即使设置了代理也没用:
public func downloadTaskWithRequest(request: NSURLRequest,
completionHandler: (NSURL?, NSURLResponse?, NSError?) -> Void)-> NSURLSessionDownloadTask
4.带handler的URL请求:
public func downloadTaskWithURL(url: NSURL, completionHandler:
(NSURL?, NSURLResponse?, NSError?) -> Void)-> NSURLSessionDownloadTask
二.task分类讲解
下面讲解每种task的具体使用方法
NSURLSession的代理继承关系如图:2-1
1.数据任务Data task
使用:
letconfig =NSURLSessionConfiguration.defaultSessionConfiguration()
leturl = NSURL(string:datadUrlNeighbor)
task.resume()
进入代理方法顺序:
1.首先进入NSURLSessionDataDelegate的URLSession:dataTask:didReceiveResponse:completionHandler方法.我们要手动在这里调用completionHandler(.Allow).系统才会继续进入下一步的代理方法,否则到此就结束了
func URLSession(session: NSURLSession, dataTask:
NSURLSessionDataTask, didReceiveResponse response: NSURLResponse,
completionHandler: (NSURLSessionResponseDisposition) -> Void){
//这个方法,只有在session的task是datatask的时候才会进入
completionHandler(.Allow)
}
2.然后进入URLSession:dataTask:didReceiveData:方法,获得json数据,可以做业务操作
funcURLSession(session:NSURLSession, dataTask:NSURLSessionDataTask, didReceiveDatadata:NSData) {
print("did receivedata")
let dic = try?NSJSONSerialization.JSONObjectWithData(data,
options: .MutableContainers)
print("get dic:\(dic!)")
}
3.然后进入URLSession:task:didCompleteWithError方法,它是NSURLSessionTaskDelegate的方法,表示请求结束了
funcURLSession(session:NSURLSession, task:NSURLSessionTask, didCompleteWithError
error:NSError?){
print("did complete")
}
2.下载任务Download task
使用:
letconfig =NSURLSessionConfiguration.defaultSessionConfiguration()
letsession = NSURLSession(configuration: config,delegate: self, delegateQueue:NSOperationQueue.mainQueue())
//方式一:不使用handler会进入代理方法
leturl = NSURL(string:datadUrlNeighbor)
letdownloadTask = session.downloadTaskWithURL(url!)
downloadTask.resume()
进入代理方法顺序:
1.NSURLSessionDownloadDelegate的URLSession:downloadTask:didWriteData:totalBytesWritten: totalBytesExpectedToWrite:
2.,数据正在写入沙盒的tmp文件夹,就会调用这个方法.它是分批次写入的,每写入一段数据就调用这个方法一次,所以会被多次调用
参数解释:
bytesWritten是本次写入的数据长度
totalBytesWritten是已经写在磁盘上的长度
totalBytesExpectedToWrite是数据本来的长度
func URLSession(session: NSURLSession,
downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64,
totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64){
print("didwrite:\(bytesWritten),\(totalBytesWritten),\(totalBytesWritten),\(totalBytesExpectedToWrite)")
}
2.NSURLSessionDownloadDelegate的URLSession:downloadTask:didFinishDownloadingToURL方法,当数据下载完成后,此时的文件是一个以tmp结尾的文件,命名类似于:
注意:进入这个方法didFinishDownloadingToURL.在这里必须执对tmp文件的转移处理,否则当除了这个方法后,tmp文件就被删除了.
func URLSession(session:NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURLlocation: NSURL) {
print("download to url:\(location)")
self.moveToCache(location, name:"ivy.zip")
self.tintLabel.text ="download to url"
}
3.进入URLSession:downloadTask:didCompleteWithError方法
func URLSession(session:
NSURLSession,task: NSURLSessionTask,didCompleteWithError error: NSError?){
}
3.上传
由于上传本人研究不深入,暂时不写,以后更新本节
三.断点继传
1.开始使用:
注意:
这里和前面下载的区别是:
1.task设置为成员变量了,因为在中断的时候,需要这个task来调用cancelByProducingResumeData:
2.session也设置为了成员变量,因为在继传的时候,需要用这个session来调用downloadTaskWithResumeData,开启一个新的downloadtask
//经测试这里写backgroundconfig和defaultconfig都可以
//let config =
NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(backgroundId)
letconfig =NSURLSessionConfiguration.defaultSessionConfiguration()
downloadSession =
NSURLSession(configuration: config,delegate:self, delegateQueue: NSOperationQueue.mainQueue())
leturl = NSURL(string:downloadUrlNeighbor)
downloadSessionTask =downloadSession!.downloadTaskWithURL(url!)
downloadSessionTask!.resume()
执行顺序:
2.按下中止按钮:
在回调块中,保存self.resumeData,便于在继传时使用
downloadSessionTask?.cancelByProducingResumeData({ (data:NSData?)in
self.resumeData=data
self.downloadSessionTask=nil//downloadSessionTask已经没用了要置为nil,因为下次继传时会由session新开一个task
})
如果不按下中止按钮,它进入代理方法的顺序和正常下载是完全一样的.即先进入didWriteData,然后进入didFinishDownloadingToURL,最后didCompleteWithError
如果按下中止按钮,会发生:
1.进入URLSession:downloadTask:didWriteData,毕竟也是写了一些数据的
2.会进cancelByProducingResumeData的回调块,在回调块里,我们要记录下resumedata,这是继传时要传入的参数,还要设置成员downloadSessionTask为nil,因为下次的继传会由session创建一个新的task,通过调用downloadTaskWithResumeData
3.进入代理URLSession:task:didCompleteWithError方法
3.继传
比如我们用一个按钮来启动继传,其中的代码如下:
就是保存的成员变量task调用downloadTaskWithResumeData:方法
@IBAction func clickGoOn(sender:AnyObject) {
guardself.resumeData!=nilelse{
return
}
//这样写可以进入代理:1 didFinishDownloadingToURL 2 didCompleteWithError
downloadSessionTask = downloadSession?.downloadTaskWithResumeData(self.resumeData!)
downloadSessionTask?.resume()
}
由于downloadTaskWithResumeData也有2种方式,带handler和不带handler的,如果调用了带有handler的那个,则不进入代理方法
进入代理方法的顺序:
1.URLSession:downloadTask:didResumeAtOffset
func URLSession(session:NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffsetfileOffset: Int64, expectedTotalBytes: Int64) {
print("didresume:\(fileOffset),total:\(expectedTotalBytes)")
}
2.URLSession:downloadTask:didWriteData又开始写入磁盘了
func URLSession(session:
NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData
bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite:
Int64){
print("didwrite:\(bytesWritten),\(totalBytesWritten),\(totalBytesExpectedToWrite)")
}
3.URLSession:downloadTask:didFinishDownloadingToURL这个比较重要,一次下载可以多次中止,但是只有全部写入成功后才会进入这个代理,在这里将下载的文件转移走,不然出了这个方法会被删除的
func URLSession(session:NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURLlocation: NSURL) {
print("download to url:\(location)")
self.resumeUrl = location
self.moveToCache(self.resumeUrl,
name:"ivy.zip")
self.tintLabel.text ="download to url"
}
4.进入URLSession:task:didCompleteWithError这回是真的下载完成了
func URLSession(session: NSURLSession, task:NSURLSessionTask, didCompleteWithErrorerror: NSError?){
print("did complete")
}
四.后台下载
在下载过程中,如果按home键将app切换到后台,只要不杀死程序,session还能保持其下载能力,完成后,通知到appdelegate的handleEventsForBackgroundURLSession方法.,虽然app不会因此回到前端.
PS:据我所知,ios只允许后台程序活跃10分钟,(除了音频,电话,地图除外),而且还不定是连续的10分钟,那么,NSURLSession的后台下载是否包含在这10分钟内呢?我不是很好做测试啊.
注意:
1.后台config必须使用backgroundSessionConfigurationWithIdentifier,要传入一个唯一的标识符
2.有几个下载任务就要创建几个config和session.每个任务都需要一个独立标示的config,以及session
3. session必须设置delegate
4.只支持HTTP/HTTPS模式
5.只支持从文件上传,不支持从data上传
(也就是只能用这个函数:funcuploadTaskWithRequest(request: NSURLRequest, fromFile fileURL: NSURL,completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) ->NSURLSessionUploadTask
而不能用这个函数:func uploadTaskWithRequest(request: NSURLRequest,fromFile fileURL: NSURL, completionHandler: (NSData?, NSURLResponse?, NSError?)-> Void) -> NSURLSessionUploadTask)
6.请用真机调试,模拟器按下home键之后不会有效果,而是会一直在delegate里下载直到完成为止
使用:
letconfig =NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(backgroundId)
letsession = NSURLSession(configuration:config,
delegate:self, delegateQueue:NSOperationQueue.mainQueue())
leturl = NSURL(string:downloadUrlNeighbor)
lettask=session.downloadTaskWithURL(url!)
task.resume()
进入代理顺序:
1.开始下载时
进入NSURLSessionDownloadDelegate代理的.URLSession:downloadTaskdidWriteData方法
2.按下home,APP进入后台
代理的.URLSession:downloadTaskdidWriteData方法不再被进入,而是程序后台静默下载
3.下载完成后
第一步:进入AppDelegate的方法:
func application(application: UIApplication,
handleEventsForBackgroundURLSession identifier: String, completionHandler: ()
-> Void){
//self.downloadCompletionHandler = completionHandler
print("----application hadle
event")
let config = NSURLSessionConfiguration.backgroundSessionConfiguration(identifier)
//The new session is automatically reassociated with ongoing backgroundactivity.
//这个session被自动绑定到了后台运行的app
let session = NSURLSession(configuration: config , delegate:self.mySessionDelegate,
delegateQueue:NSOperationQueue.mainQueue())
//去使用的类里面注册一个handler,直接传过去也可以的,其实self.window.rootViewController = xxx也可以的
self.mySessionDelegate.addCompletionHandler(completionHandler, identifier:
identifier)
}
解释:
1.保存handler的方式是多种的,可以给AppDelegate设置一个dictionary的属性.也可以传入session的delegate的dictionary属性,我选择了后者
2.要创建一个session,文档说这个session会被自动绑定到后台的activity
第二步:
进入NSURLSessionDownloadDelegate的didFinishDownloadingToURL方法,请在这里搬运下载好的tmp文件
第三步:进入URLSession:task:didCompleteWithError
第四步:进入NSURLSessionDelegate的URLSessionDidFinishEventsForBackgroundURLSession方法
func URLSessionDidFinishEventsForBackgroundURLSession(session:NSURLSession)
{
print("did
finish events")
if (session.configuration.identifier!= nil) {
let handler = self.completionHandlerDictionary![session.configuration.identifier!]
guard handler != nil else {
return
}
handler!()
//移除dictionary中的数据
self.completionHandlerDictionary?.removeValueForKey(session.configuration.identifier!)
self.tintLabel.text="finish event"
}
}
在第三步或第四步里面,把从appdelegate里面获取到的handler执行一下,这么做的目的,文档告诉我们是为了让操作系统知道程序可以被继续安全的挂起.
参考文档:
demo:https://github.com/ivychenyucong/TestNSURLSession
ps:打算翻译下那篇参考文档含金量挺高的