一.概述
NSURLSession始于ios7.它具有訪問接口,上傳/下載數(shù)據(jù),斷點繼傳和后臺下載等功能:其使用步驟:
1.創(chuàng)建session指定其configuration
2.由session執(zhí)行任務(wù)得到task
3. task調(diào)用resume,啟動網(wǎng)絡(luò)請求
1.task分類
session的任務(wù)有四種:
1.數(shù)據(jù)任務(wù)Data task
2.下載任務(wù)Download task
3.上傳任務(wù)Upload task
4.流任務(wù)Stream task ios9之后出現(xiàn)的,用于TCP/IP流
2. configuration類型
configuration的類型有三種:
1.默認(rèn)配置Defaultsessions:
使用磁盤緩存,用將證書存在用戶的鑰匙串
2.即時配置Ephemeralsessions:
不使用磁盤緩存,也存儲證書,它的信息存于RAM中,如果session被invalidate,這些信息也被清理掉
3.后臺配置Background sessions:
配置上同默認(rèn)配置,但是有一個獨立進(jìn)程來操作上傳/下載
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,就不會進(jìn)入代理方法,即使設(shè)置了代理也沒用:
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的代理繼承關(guān)系如圖:2-1
1.數(shù)據(jù)任務(wù)Data task
使用:
letconfig =NSURLSessionConfiguration.defaultSessionConfiguration()
leturl = NSURL(string:datadUrlNeighbor)
task.resume()
進(jìn)入代理方法順序:
1.首先進(jìn)入NSURLSessionDataDelegate的URLSession:dataTask:didReceiveResponse:completionHandler方法.我們要手動在這里調(diào)用completionHandler(.Allow).系統(tǒng)才會繼續(xù)進(jìn)入下一步的代理方法,否則到此就結(jié)束了
func URLSession(session: NSURLSession, dataTask:
NSURLSessionDataTask, didReceiveResponse response: NSURLResponse,
completionHandler: (NSURLSessionResponseDisposition) -> Void){
//這個方法,只有在session的task是datatask的時候才會進(jìn)入
completionHandler(.Allow)
}
2.然后進(jìn)入URLSession:dataTask:didReceiveData:方法,獲得json數(shù)據(jù),可以做業(yè)務(wù)操作
funcURLSession(session:NSURLSession, dataTask:NSURLSessionDataTask, didReceiveDatadata:NSData) {
print("did receivedata")
let dic = try?NSJSONSerialization.JSONObjectWithData(data,
options: .MutableContainers)
print("get dic:\(dic!)")
}
3.然后進(jìn)入URLSession:task:didCompleteWithError方法,它是NSURLSessionTaskDelegate的方法,表示請求結(jié)束了
funcURLSession(session:NSURLSession, task:NSURLSessionTask, didCompleteWithError
error:NSError?){
print("did complete")
}
2.下載任務(wù)Download task
使用:
letconfig =NSURLSessionConfiguration.defaultSessionConfiguration()
letsession = NSURLSession(configuration: config,delegate: self, delegateQueue:NSOperationQueue.mainQueue())
//方式一:不使用handler會進(jìn)入代理方法
leturl = NSURL(string:datadUrlNeighbor)
letdownloadTask = session.downloadTaskWithURL(url!)
downloadTask.resume()
進(jìn)入代理方法順序:
1.NSURLSessionDownloadDelegate的URLSession:downloadTask:didWriteData:totalBytesWritten: totalBytesExpectedToWrite:
2.,數(shù)據(jù)正在寫入沙盒的tmp文件夾,就會調(diào)用這個方法.它是分批次寫入的,每寫入一段數(shù)據(jù)就調(diào)用這個方法一次,所以會被多次調(diào)用
參數(shù)解釋:
bytesWritten是本次寫入的數(shù)據(jù)長度
totalBytesWritten是已經(jīng)寫在磁盤上的長度
totalBytesExpectedToWrite是數(shù)據(jù)本來的長度
func URLSession(session: NSURLSession,
downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64,
totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64){
print("didwrite:\(bytesWritten),\(totalBytesWritten),\(totalBytesWritten),\(totalBytesExpectedToWrite)")
}
2.NSURLSessionDownloadDelegate的URLSession:downloadTask:didFinishDownloadingToURL方法,當(dāng)數(shù)據(jù)下載完成后,此時的文件是一個以tmp結(jié)尾的文件,命名類似于:
注意:進(jìn)入這個方法didFinishDownloadingToURL.在這里必須執(zhí)對tmp文件的轉(zhuǎn)移處理,否則當(dāng)除了這個方法后,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.進(jìn)入URLSession:downloadTask:didCompleteWithError方法
func URLSession(session:
NSURLSession,task: NSURLSessionTask,didCompleteWithError error: NSError?){
}
3.上傳
由于上傳本人研究不深入,暫時不寫,以后更新本節(jié)
三.斷點繼傳
1.開始使用:
注意:
這里和前面下載的區(qū)別是:
1.task設(shè)置為成員變量了,因為在中斷的時候,需要這個task來調(diào)用cancelByProducingResumeData:
2.session也設(shè)置為了成員變量,因為在繼傳的時候,需要用這個session來調(diào)用downloadTaskWithResumeData,開啟一個新的downloadtask
//經(jīng)測試這里寫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()
執(zhí)行順序:
2.按下中止按鈕:
在回調(diào)塊中,保存self.resumeData,便于在繼傳時使用
downloadSessionTask?.cancelByProducingResumeData({ (data:NSData?)in
self.resumeData=data
self.downloadSessionTask=nil//downloadSessionTask已經(jīng)沒用了要置為nil,因為下次繼傳時會由session新開一個task
})
如果不按下中止按鈕,它進(jìn)入代理方法的順序和正常下載是完全一樣的.即先進(jìn)入didWriteData,然后進(jìn)入didFinishDownloadingToURL,最后didCompleteWithError
如果按下中止按鈕,會發(fā)生:
1.進(jìn)入URLSession:downloadTask:didWriteData,畢竟也是寫了一些數(shù)據(jù)的
2.會進(jìn)cancelByProducingResumeData的回調(diào)塊,在回調(diào)塊里,我們要記錄下resumedata,這是繼傳時要傳入的參數(shù),還要設(shè)置成員downloadSessionTask為nil,因為下次的繼傳會由session創(chuàng)建一個新的task,通過調(diào)用downloadTaskWithResumeData
3.進(jìn)入代理URLSession:task:didCompleteWithError方法
3.繼傳
比如我們用一個按鈕來啟動繼傳,其中的代碼如下:
就是保存的成員變量task調(diào)用downloadTaskWithResumeData:方法
@IBAction func clickGoOn(sender:AnyObject) {
guardself.resumeData!=nilelse{
return
}
//這樣寫可以進(jìn)入代理:1 didFinishDownloadingToURL 2 didCompleteWithError
downloadSessionTask = downloadSession?.downloadTaskWithResumeData(self.resumeData!)
downloadSessionTask?.resume()
}
由于downloadTaskWithResumeData也有2種方式,帶handler和不帶handler的,如果調(diào)用了帶有handler的那個,則不進(jìn)入代理方法
進(jìn)入代理方法的順序:
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這個比較重要,一次下載可以多次中止,但是只有全部寫入成功后才會進(jìn)入這個代理,在這里將下載的文件轉(zhuǎn)移走,不然出了這個方法會被刪除的
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.進(jìn)入URLSession:task:didCompleteWithError這回是真的下載完成了
func URLSession(session: NSURLSession, task:NSURLSessionTask, didCompleteWithErrorerror: NSError?){
print("did complete")
}
四.后臺下載
在下載過程中,如果按home鍵將app切換到后臺,只要不殺死程序,session還能保持其下載能力,完成后,通知到appdelegate的handleEventsForBackgroundURLSession方法.,雖然app不會因此回到前端.
PS:據(jù)我所知,ios只允許后臺程序活躍10分鐘,(除了音頻,電話,地圖除外),而且還不定是連續(xù)的10分鐘,那么,NSURLSession的后臺下載是否包含在這10分鐘內(nèi)呢?我不是很好做測試啊.
注意:
1.后臺config必須使用backgroundSessionConfigurationWithIdentifier,要傳入一個唯一的標(biāo)識符
2.有幾個下載任務(wù)就要創(chuàng)建幾個config和session.每個任務(wù)都需要一個獨立標(biāo)示的config,以及session
3. session必須設(shè)置delegate
4.只支持HTTP/HTTPS模式
5.只支持從文件上傳,不支持從data上傳
(也就是只能用這個函數(shù):funcuploadTaskWithRequest(request: NSURLRequest, fromFile fileURL: NSURL,completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) ->NSURLSessionUploadTask
而不能用這個函數(shù):func uploadTaskWithRequest(request: NSURLRequest,fromFile fileURL: NSURL, completionHandler: (NSData?, NSURLResponse?, NSError?)-> Void) -> NSURLSessionUploadTask)
6.請用真機(jī)調(diào)試,模擬器按下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()
進(jìn)入代理順序:
1.開始下載時
進(jìn)入NSURLSessionDownloadDelegate代理的.URLSession:downloadTaskdidWriteData方法
2.按下home,APP進(jìn)入后臺
代理的.URLSession:downloadTaskdidWriteData方法不再被進(jìn)入,而是程序后臺靜默下載
3.下載完成后
第一步:進(jìn)入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設(shè)置一個dictionary的屬性.也可以傳入session的delegate的dictionary屬性,我選擇了后者
2.要創(chuàng)建一個session,文檔說這個session會被自動綁定到后臺的activity
第二步:
進(jìn)入NSURLSessionDownloadDelegate的didFinishDownloadingToURL方法,請在這里搬運下載好的tmp文件
第三步:進(jìn)入URLSession:task:didCompleteWithError
第四步:進(jìn)入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中的數(shù)據(jù)
self.completionHandlerDictionary?.removeValueForKey(session.configuration.identifier!)
self.tintLabel.text="finish event"
}
}
在第三步或第四步里面,把從appdelegate里面獲取到的handler執(zhí)行一下,這么做的目的,文檔告訴我們是為了讓操作系統(tǒng)知道程序可以被繼續(xù)安全的掛起.
參考文檔:
demo:https://github.com/ivychenyucong/TestNSURLSession
ps:打算翻譯下那篇參考文檔含金量挺高的