NSURLSession是蘋果在WWDC2013中提出來的,旨在替代NSURLConnection,與咱們以前常用的NSURLConnection不一樣,NSURLSession爲咱們提供了更靈活的使用方法,包括後臺下載以及斷點續傳的實現等功能.以前使用下載一直用的都是第三方框架好比OC的AFNetworking或者Swift的Alamofire.雖然第三方庫用起來很方便也很穩定,可是仍是想本身研究下蘋果原生的下載框架.這幾天研究了下NSURLSession,作了一個加載視頻的demo,包括了視頻下載,斷點續傳的功能,本身簡單總結下,也算是對本身學習過程的一個記錄,嘿嘿.git
和使用NSURLConnection下載的時候建立NSURLConnection對象以前發起同步或者異步請求不一樣,NSURLSession咱們使用的是它的三個子類,先簡單說下NSURLSession的使用流程吧:github
具體介紹下這三個東西是用來幹嗎的:緩存
NSURLSessionConfiguration有個重要的屬性markdown
/* allow request to route over cellular. */ @property BOOL allowsCellularAccess; /* allows background tasks to be scheduled at the discretion of the system for optimal performance. */ @property (getter=isDiscretionary) BOOL discretionary NS_AVAILABLE(10_10, 7_0);
allowsCellularAccess屬性爲是否容許在移動網絡下下載文件,通常狀況下建議使用discretionary,該屬性容許應用自動判斷當前使用哪一種網絡下載比較好.網絡
+ (NSURLSession *)sharedSession; 建立全局的session,返回共享的會話,使用全局的Cache、Cookie和證書 + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration; 使用本身建立的配置文件建立session + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue; 使用本身建立的配置文件建立session,而且能夠設置他的代理,用來監聽他的代理事件,從而監聽下載進度以及將下載好的文件轉移等操做,用起來十分方便,也是咱們最常常用的一種模式
重點是第三種建立方法,這也是最常用的方法session
+(NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration
delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;
重點說一下其參數意義:
1. configuration:咱們建立的配置文件,傳進去就能夠了
2. delegate: 通常設置本身爲其代理
3. delegateQueue: 方法回調隊列,這個須要咱們傳一個NSOperationQueue隊列進去,其代理回調函數都會在咱們傳進去的隊列執行.若是咱們傳nil的話,其代理回調函數則會在添加到全局併發隊列裏去,將會開啓多個線程來執行任務下載.併發
NSURLSessionDataTask NSURLSessionUploadTask NSURLSessionDownloadTask
做用不言而喻,上傳時候使用NSURLSessionUploadTask,下載的時候使用NSURLSessionDownloadTask,至於NSURLSessionDataTask,咱們看下蘋果的官方說明:app
/*
* An NSURLSessionDataTask does not provide any additional * functionality over an NSURLSessionTask and its presence is merely * to provide lexical differentiation from download and upload tasks. */ @interface NSURLSessionDataTask : NSURLSessionTask
翻譯:相比於NSURLSessionTask她不提額外的功能,它的存在僅僅是爲了和download以及upload詞彙方面的不一樣. 呵呵噠框架
基本上,咱們上傳和下載使用那兩個類就足夠了,至於這個爲了提供詞彙不一樣的NSURLSessionDataTask,基本上用不到.iphone
以NSURLSessionDownloadTask爲例,咱們看下他的建立方法:
/* Creates a download task with the given request. */ 使用咱們建立好的request來建立下載任務,和NSURLConnection使用方法相似 - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request; /* Creates a download task to download the contents of the given URL. */ 直接使用連接建立下載任務 - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url; /* Creates a download task with the resume data. If the download cannot be successfully resumed, URLSession:task:didCompleteWithError: will be called. */ 使用續傳數據來建立下載任務 - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
建立好以後,開啓任務的方法:
[self.downloadTask resume]; //開始下載文件
示例代碼:
//建立configuration NSURLSessionConfiguration *configuration ==[NSURLSessionConfiguration defaultSessionConfiguration]; configuration.discretionary = YES; //使用建立的configuration建立session 並將本身設置爲代理 NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; //建立request k_DownloadURLStr是我定義的宏定義網址連接 NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:k_DownloadURLStr]]; //使用建立的session和request建立NSURLSessionDownloadTask NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request]; //開啓下載任務 [downloadTask resume]; [downloadTask invalidateAndCancel]; 關閉下載任務 [downloadTask finishTasksAndInvalidate]; 等待當前Task結束後關閉
既然設置本身爲session的代理,須要實現的代理協議爲NSURLSessionDownloadDelegate,通常使用NSURLSessionDownloadDelegate代理中的如下三個方法:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite 當下載任務開啓以後,一旦有數據返回,那麼這個方法就會被調用,其參數意義以下: bytesWritten : 本次調用返回了多少字節的數據 totalBytesWritten: 一共返回了多少字節的數據 totalBytesExpectedToWrite: 該下載文件的大小 經過這個方法,咱們就能夠實時的監聽下載的進度了
當下載完成以後,會調用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
值得一提的是,當文件正在下載的時候,文件是下載到沙盒中的tmp文件夾中的,當下載完成以後,文件仍然存在於該文件夾中.可是該文件夾中的內容當iphone從新啓動的時候就會被清空,因此咱們須要將下載好的文件轉移到cache文件夾中保存起來,給出一個操做示例
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSURL *urlOfSave = [NSURL fileURLWithPath:paths[0]]; urlOfSave = [urlOfSave URLByAppendingPathComponent:@"冰河世紀.mov"]; NSLog(@"%@",urlOfSave); NSFileManager *fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath:urlOfSave.path]) { //若是文件夾下有同名文件 則將其刪除 [fileManager removeItemAtURL:urlOfSave error:nil]; } //將下載好的文件複製到存儲的文件夾下 [fileManager copyItemAtURL:location toURL:urlOfSave error:nil]; [self.session invalidateAndCancel]; self.session = nil; }
當咱們關閉任務或者手動結束任務的時候,會調用:
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error;
其中resumeData中保存的並非當前下載的文件,將其拿到轉出來的內容是xml報文,內容以下所示,(部分冗餘的內容我已經切掉了),部分地方我已經添加註釋.若是要實現斷點續傳功能的話,首先要調用方法
__weak typeof (self)weakSelf = self; [self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) { weakSelf.downloadData = resumeData; }]; downloadData是我建立的全局變量,用來保存中斷的數據, 接着須要使用方法 self.downloadTask = [self.session downloadTaskWithResumeData:self.downloadData]; [self.downloadTask resume]; 來從新開啓下載任務
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>NSURLSessionDownloadURL</key> <string>http://oarbi0614.bkt.clouddn.com/%E5%86%B0%E6%B2%B3%E4%B8%96%E7%BA%AA.mp4</string> 請求的地址 <key>NSURLSessionResumeBytesReceived</key> <integer>12038723</integer> 當前下載的文件大小 <key>NSURLSessionResumeCurrentRequest</key> <data> YnBsaXN0MDD..... </data> <key>NSURLSessionResumeEntityTag</key> <string>"ljECR82nRMhHvP8D5M9sGQuKBjgK"</string> <key>NSURLSessionResumeInfoTempFileName</key> <string>CFNetworkDownload_6XdKfZ.tmp</string> 下載使用的臨時文件名 <key>NSURLSessionResumeInfoVersion</key> <integer>2</integer> <key>NSURLSessionResumeOriginalRequest</key> <data> YnBsaXN0MDD... </data> <key>NSURLSessionResumeServerDownloadDate</key> <string>Mon, 25 Jul 2016 10:42:23 GMT</string> </dict> </plist>
當咱們暫停以後拿到resumeData,再從新使用resumeData下載的時候,session會使用該數據塊中的信息去查找臨時文件,而後繼續下載.這樣的話,咱們也就實現了斷點續傳的功能.
延伸
這個雖然實現了斷點續傳的功能,可是若是應用在運行的時候被殺死,那麼下次下載的時候仍然是須要從新下載,上次下載的數據仍然會丟失,因此說,該斷點續傳僅僅是使用在程序正常運行過程當中的暫停而後能夠接着下載,若是程序意外終結那麼下載數據仍然會丟失.關於這個問題,我想了不少的解決方案,在下一篇博客,我會具體說明我是如何實現的,也歡迎你們隨時指教.
結語
上面的動圖是我作的一個demo,包括了視頻的下載,播放,暫停,恢復播放的功能.其使用的加載百分比視圖是本身封裝的一個小控件,視頻播放使用的是AVPlayer.該demo放在了gitHub上面,地址:https://github.com/TheRuningAnt/TestSession.git 歡迎各位下載查看.喜歡的話記得給我一個星星哦.另外,該博文中有任何錯誤或不足之處,還請各位看官不吝賜教.多謝!