原文鏈接:http://blog.csdn.net/mumubumaopao/article/details/52074367
NSURLSession是蘋果在WWDC2013中提出來(lái)的,旨在替代NSURLConnection,與我們之前經(jīng)常使用的NSURLConnection不同,NSURLSession為我們提供了更靈活的使用方法,包括后臺(tái)下載以及斷點(diǎn)續(xù)傳的實(shí)現(xiàn)等功能.之前使用下載一直用的都是第三方框架比如OC的AFNetworking或者
的Alamofire.雖然第三方庫(kù)用起來(lái)很方便也很穩(wěn)定,但是還是想自己研究下蘋果原生的下載框架.這幾天研究了下NSURLSession,做了一個(gè)加載視頻的demo,包括了視頻下載,斷點(diǎn)續(xù)傳的功能,自己簡(jiǎn)單總結(jié)下,也算是對(duì)自己學(xué)習(xí)過程的一個(gè)記錄,嘿嘿.
注:iOS9中引入了新特性App Transport Security (ATS),該特性要求App內(nèi)訪問網(wǎng)站必須使用HTTPS協(xié)議拆吆。我們可以暫時(shí)關(guān)閉該協(xié)議:
1. 在Info.plist中添加新項(xiàng)NSAppTransportSecurity架诞,類型為Dictionary踢星。
2. 在NSAppTransportSecurity下添加子項(xiàng)NSAllowsArbitraryLoads馍迄,類型為Boolean,值設(shè)為YES。
和使用NSURLConnection下載的時(shí)候創(chuàng)建NSURLConnection對(duì)象之前發(fā)起同步或者異步請(qǐng)求不同,NSURLSession我們使用的是它的三個(gè)子類,先簡(jiǎn)單說下NSURLSession的使用流程吧:
創(chuàng)建 NSURLSessionConfiguration
使用 NSURLSessionConfiguration創(chuàng)建NSURLSession
使用NSURLSession來(lái)創(chuàng)建NSURLSessionTask對(duì)象
使用NSURLSessionTask對(duì)象來(lái)下載文件
具體介紹下這三個(gè)東西是用來(lái)干嘛的:
NSURLSessionConfiguration
NSURLSessionConfiguration是NSURLSession的配置文件,其中包括了緩存策略,請(qǐng)求超時(shí)時(shí)長(zhǎng)以及使用什么網(wǎng)絡(luò)類型請(qǐng)求等屬性,其創(chuàng)建方法有三種:
+(NSURLSessionConfiguration *)defaultSessionConfiguration; //默認(rèn)模式,可以使用緩存的Cache,Cookie
+(NSURLSessionConfiguration *)ephemeralSessionConfiguration;//瞬時(shí)會(huì)話模式 不可以使用緩存的Cache,Cookie殴泰,鑒權(quán)
+(NSURLSessionConfiguration*)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier NS_AVAILABLE(10_10, 8_0);//后臺(tái)模式 可以在后臺(tái)進(jìn)行下載
NSURLSessionConfiguration有個(gè)重要的屬性
/* allow requesttorouteovercellular. */@propertyBOOL allowsCellularAccess;? /* allows background taskstobe scheduledatthediscretionofthesystemforoptimal performance. */@property(getter=isDiscretionary) BOOL discretionary NS_AVAILABLE(10_10,7_0);
allowsCellularAccess屬性為是否允許在移動(dòng)網(wǎng)絡(luò)下下載文件,一般情況下建議使用discretionary,該屬性允許應(yīng)用自動(dòng)判斷當(dāng)前使用哪種網(wǎng)絡(luò)下載比較好.
NSURLSession
NSURLSession是用來(lái)生成和管理任務(wù)的,它有三種創(chuàng)建模式,如下所示
+(NSURLSession *)sharedSession;創(chuàng)建全局的session,返回共享的會(huì)話,使用全局的Cache叫挟、Cookie和證書+(NSURLSession*)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;使用自己創(chuàng)建的配置文件創(chuàng)建session+(NSURLSession*)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id)delegate delegateQueue:(nullable NSOperationQueue *)queue;使用自己創(chuàng)建的配置文件創(chuàng)建session,并且可以設(shè)置他的代理,用來(lái)監(jiān)聽他的代理事件,從而監(jiān)聽下載進(jìn)度以及將下載好的文件轉(zhuǎn)移等操作,用起來(lái)十分方便,也是我們最經(jīng)常用的一種模式
重點(diǎn)是第三種創(chuàng)建方法,這也是最經(jīng)常使用的方法
+(NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configurationdelegate:(nullableid )delegatedelegateQueue:(nullableNSOperationQueue *)queue;
重點(diǎn)說一下其參數(shù)意義:
1. configuration:我們創(chuàng)建的配置文件,傳進(jìn)去就可以了
2. delegate: 一般設(shè)置自己為其代理
3. delegateQueue: 方法回調(diào)隊(duì)列,這個(gè)需要我們傳一個(gè)NSOperationQueue隊(duì)列進(jìn)去,其代理回調(diào)函數(shù)都會(huì)在我們傳進(jìn)去的隊(duì)列執(zhí)行.如果我們傳nil的話,其代理回調(diào)函數(shù)則會(huì)在添加到全局并發(fā)隊(duì)列里去,將會(huì)開啟多個(gè)線程來(lái)執(zhí)行任務(wù)下載.
NSURLSessionTask
任務(wù)對(duì)象,也是我們開啟下載的對(duì)象,它有三個(gè)子類,我們使用的時(shí)候也是直接使用它的子類
NSURLSessionDataTask
NSURLSessionUploadTask
NSURLSessionDownloadTask
作用不言而喻,上傳時(shí)候使用NSURLSessionUploadTask,下載的時(shí)候使用NSURLSessionDownloadTask,至于NSURLSessionDataTask,我們看下蘋果的官方說明:
/* * An NSURLSessionDataTaskdoesnotprovide any additional * functionalityoveran NSURLSessionTaskanditspresenceismerely *toprovide lexical differentiationfromdownloadandupload tasks. */@interface NSURLSessionDataTask : NSURLSessionTask
翻譯:相比于NSURLSessionTask她不提額外的功能,它的存在僅僅是為了和download以及upload詞匯方面的不同. 呵呵噠
基本上,我們上傳和下載使用那兩個(gè)類就足夠了,至于這個(gè)為了提供詞匯不同的NSURLSessionDataTask,基本上用不到.
以NSURLSessionDownloadTask為例,我們看下他的創(chuàng)建方法:
/* Creates a download taskwiththegivenrequest. */使用我們創(chuàng)建好的request來(lái)創(chuàng)建下載任務(wù),和NSURLConnection使用方法類似- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;/* Creates a download tasktodownloadthecontentsofthegivenURL. */直接使用鏈接創(chuàng)建下載任務(wù)- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url;/* Creates a download taskwiththeresume data.? Ifthedownload cannot be successfully resumed, URLSession:task:didCompleteWithError: will be called. */使用續(xù)傳數(shù)據(jù)來(lái)創(chuàng)建下載任務(wù)- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
創(chuàng)建好之后,開啟任務(wù)的方法:
[self.downloadTask resume]; //開始下載文件
創(chuàng)建示例
說了這么多,來(lái)看下具體的使用示例吧,提供一個(gè)簡(jiǎn)單完整的創(chuàng)建流程和開啟任務(wù)的方法,保證看完秒懂 0.0:
示例代碼://創(chuàng)建configurationNSURLSessionConfiguration *configuration ==[NSURLSessionConfiguration defaultSessionConfiguration];? ? ? ? configuration.discretionary=YES;//使用創(chuàng)建的configuration創(chuàng)建session 并將自己設(shè)置為代理NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:selfdelegateQueue:nil];//創(chuàng)建request k_DownloadURLStr是我定義的宏定義網(wǎng)址鏈接NSURLRequest*request = [NSURLRequestrequestWithURL:[NSURLURLWithString:k_DownloadURLStr]];//使用創(chuàng)建的session和request創(chuàng)建NSURLSessionDownloadTaskNSURLSessionDownloadTask *downloadTask = [self.sessiondownloadTaskWithRequest:request];//開啟下載任務(wù)[downloadTask resume];[downloadTask invalidateAndCancel];? 關(guān)閉下載任務(wù)[downloadTask finishTasksAndInvalidate]; 等待當(dāng)前Task結(jié)束后關(guān)閉
既然設(shè)置自己為session的代理,需要實(shí)現(xiàn)的代理協(xié)議為NSURLSessionDownloadDelegate,一般使用NSURLSessionDownloadDelegate代理中的以下三個(gè)方法:
-(void)URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask*)downloadTask? ? ? didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWrittentotalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite當(dāng)下載任務(wù)開啟之后,一旦有數(shù)據(jù)返回,那么這個(gè)方法就會(huì)被調(diào)用,其參數(shù)意義如下:bytesWritten :? ? ? ? ? ? ? 本次調(diào)用返回了多少字節(jié)的數(shù)據(jù)totalBytesWritten:? ? ? ? ? 一共返回了多少字節(jié)的數(shù)據(jù)totalBytesExpectedToWrite:? 該下載文件的大小通過這個(gè)方法,我們就可以實(shí)時(shí)的監(jiān)聽下載的進(jìn)度了
當(dāng)下載完成之后,會(huì)調(diào)用
-(void)URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask*)downloadTaskdidFinishDownloadingToURL:(NSURL*)location
值得一提的是,當(dāng)文件正在下載的時(shí)候,文件是下載到沙盒中的tmp文件夾中的,當(dāng)下載完成之后,文件仍然存在于該文件夾中.但是該文件夾中的內(nèi)容當(dāng)iphone重新啟動(dòng)的時(shí)候就會(huì)被清空,所以我們需要將下載好的文件轉(zhuǎn)移到cache文件夾中保存起來(lái),給出一個(gè)操作示例
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTaskdidFinishDownloadingToURL:(NSURL*)location{NSArray*paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask,YES);NSURL*urlOfSave = [NSURLfileURLWithPath:paths[0]];? ? urlOfSave = [urlOfSave URLByAppendingPathComponent:@"冰河世紀(jì).mov"];NSLog(@"%@",urlOfSave);NSFileManager*fileManager = [NSFileManagerdefaultManager];if([fileManager fileExistsAtPath:urlOfSave.path]) {//如果文件夾下有同名文件? 則將其刪除[fileManager removeItemAtURL:urlOfSave error:nil];? ? }//將下載好的文件復(fù)制到存儲(chǔ)的文件夾下[fileManager copyItemAtURL:location toURL:urlOfSave error:nil];? ? [self.sessioninvalidateAndCancel];self.session=nil;}
當(dāng)我們關(guān)閉任務(wù)或者手動(dòng)結(jié)束任務(wù)的時(shí)候,會(huì)調(diào)用:
-(void)URLSession:(NSURLSession*)session didBecomeInvalidWithError:(nullableNSError*)error;
斷點(diǎn)續(xù)傳的實(shí)現(xiàn)
如果要實(shí)現(xiàn)斷點(diǎn)續(xù)傳功能的話,首先要調(diào)用方法? ? __weaktypeof (self)weakSelf =self;? [self.downloadTaskcancelByProducingResumeData:^(NSData * _Nullable resumeData) {? ? ? ? weakSelf.downloadData= resumeData;? ? }];? ? downloadData是我創(chuàng)建的全局變量,用來(lái)保存中斷的數(shù)據(jù),? 接著需要使用方法self.downloadTask= [self.sessiondownloadTaskWithResumeData:self.downloadData];? ? [self.downloadTaskresume];? ? 來(lái)重新開啟下載任務(wù)
其中resumeData中保存的并不是當(dāng)前下載的文件,將其拿到轉(zhuǎn)出來(lái)的內(nèi)容是xml報(bào)文,內(nèi)容如下所示,(部分冗余的內(nèi)容我已經(jīng)切掉了),部分地方我已經(jīng)添加注釋.
NSURLSessionDownloadURLhttp://oarbi0614.bkt.clouddn.com/%E5%86%B0%E6%B2%B3%E4%B8%96%E7%BA%AA.mp4請(qǐng)求的地址NSURLSessionResumeBytesReceived12038723當(dāng)前下載的文件大小NSURLSessionResumeCurrentRequestYnBsaXN0MDD.....NSURLSessionResumeEntityTag"ljECR82nRMhHvP8D5M9sGQuKBjgK"NSURLSessionResumeInfoTempFileNameCFNetworkDownload_6XdKfZ.tmp下載使用的臨時(shí)文件名NSURLSessionResumeInfoVersion2NSURLSessionResumeOriginalRequestYnBsaXN0MDD...NSURLSessionResumeServerDownloadDateMon, 25 Jul 2016 10:42:23 GMT
當(dāng)我們暫停之后拿到resumeData,再重新使用resumeData下載的時(shí)候,session會(huì)使用該數(shù)據(jù)塊中的信息去查找臨時(shí)文件,然后繼續(xù)下載.這樣的話,我們也就實(shí)現(xiàn)了斷點(diǎn)續(xù)傳的功能.
延伸
這個(gè)雖然實(shí)現(xiàn)了斷點(diǎn)續(xù)傳的功能,但是如果應(yīng)用在運(yùn)行的時(shí)候被殺死,那么下次下載的時(shí)候仍然是需要重新下載,上次下載的數(shù)據(jù)仍然會(huì)丟失,所以說,該斷點(diǎn)續(xù)傳僅僅是使用在程序正常運(yùn)行過程中的暫停然后可以接著下載,如果程序意外終結(jié)那么下載數(shù)據(jù)仍然會(huì)丟失.關(guān)于這個(gè)問題,我想了很多的解決方案,在下一篇博客,我會(huì)具體說明我是如何實(shí)現(xiàn)的,也歡迎大家隨時(shí)指教.
結(jié)語(yǔ)
上面的動(dòng)圖是我做的一個(gè)demo,包括了視頻的下載,播放,暫停,恢復(fù)播放的功能.其使用的加載百分比視圖是自己封裝的一個(gè)小控件,視頻播放使用的是AVPlayer.該demo放在了gitHub上面(該demo基于iphone6s plus尺寸開發(fā),僅為展示效果,未做屏幕適配,請(qǐng)稍加留意),地址:https://github.com/TheRuningAnt/TestSession.git
歡迎各位下載查看.喜歡的話記得給我一個(gè)星星哦.另外,該博文中有任何錯(cuò)誤或不足之處,還請(qǐng)各位看官不吝賜教.多謝!
加入審核被拒交流群艰匙,一起交流審核上架經(jīng)驗(yàn)吧~~ 群號(hào):689757099