一. 背景
NSURLSession是蘋果在iOS7后推出的通過HTTP協(xié)議下載數(shù)據(jù)的API,該類提供了大量的代理方法偶摔,來支持認證和后臺下載功能瞬内。
使用NSURLSession須知:
a> 我們的應(yīng)用會創(chuàng)建一系列的對話,每個對話負責一組的數(shù)據(jù)傳輸任務(wù),并且會添加一系列的任務(wù)灿巧,每個任務(wù)代表著一個URL請求铺坞。
b> NSURLSession的API是異步的劲够。
c> NSURLSession的使用分成:1.系統(tǒng)代理的方式(需要提供一個請求完成后處理的block)2. 自定義代理的方式(執(zhí)行代理方法來處理請求成功或失敼纭)
d> NSURLSession支持取消聊疲、恢復(fù)茬底、掛起操作,以及斷掉續(xù)傳的功能
二. NSURLSession
NSURLSession根據(jù)配置對象(NSURLSessionConfiguration)分為三大類型
- 默認會話(defaultSessionConfiguration):存儲cookie(緩存)在磁盤中获洲,存儲證書在用戶keychain
- 后臺會話(backgroundSessionConfiguration):存儲cookie(緩存)在磁盤中阱表,存儲證書在用戶keychain。后臺會添加獨立的進程來處理數(shù)據(jù)傳輸任務(wù)
- 短暫會話(ephemeralSessionConfiguration):緩存和證書都存在RAM中,會話結(jié)束最爬,它們就自動釋放
任務(wù)類型也分為三大類
- dataTask:使用NSData對象進行上傳和下載數(shù)據(jù)涉馁;數(shù)據(jù)可一次性返回,也可以分片段返回烂叔;返回的數(shù)據(jù)不是進行文件存儲谨胞,所以不支持后臺會話(iOS8后支持 備注:主要用于一些小數(shù)據(jù)請求,或大文件的斷點續(xù)傳下載
- downloadTask:以文件的形式接收數(shù)據(jù)蒜鸡,當程序不運行時支持后臺下載
- uploadTask:以文件的形式上傳數(shù)據(jù)胯努,支持后臺下載
簡單使用
1. 系統(tǒng)代理的方式
a>. dataTask
// 1.創(chuàng)建NSURLSession單例對象
NSURLSession *session = [NSURLSession sharedSession];
// 2.添加dataTask任務(wù)(1>. 通過NSURLRequest方式 ;2>. 通過NSURL方式(內(nèi)部進行封裝成NSURLRequest對象))
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxx"]];
/*
POST請求請加這兩句
request.HTTPMethod = @"POST";
request.HTTPBody = [@"參數(shù)" dataUsingEncoding:NSUTF8StringEncoding];
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
/*
data:請求成功后返回的數(shù)據(jù)
response:請求頭
error:如果有值說明請求失敗
*/
}];
//或則
// NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:@"http://xxx"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//
// }];
// 3.開始任務(wù)
[dataTask resume];
b>. downLoadTask
內(nèi)部已經(jīng)實現(xiàn)邊下載邊寫入temp文件中逢防,由于temp中的文件易清除叶沛,所以須手動將文件剪切到合適的沙河目錄 缺點:無法監(jiān)控下載進度
// 1.創(chuàng)建NSURLSession單例對象
NSURLSession *session = [NSURLSession sharedSession];
// 2.添加downloadTask任務(wù)(1>. 通過NSURLRequest方式 ;2>. 通過NSURL方式(內(nèi)部進行封裝成NSURLRequest對象))
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxx"]];
/*
POST請求請加這兩句
request.HTTPMethod = @"POST";
request.HTTPBody = [@"參數(shù)" dataUsingEncoding:NSUTF8StringEncoding];
*/
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
/*
location:下載的文件的保存地址,默認是temp
*/
}];
//或則
// NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:[NSURL URLWithString:@"http://xxx"] completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//
// }];
// 3.開始任務(wù)
[downloadTask resume];
c>. upLoadTask
// 1.創(chuàng)建NSURLSession單例對象
NSURLSession *session = [NSURLSession sharedSession];
// 2.添加uploadTask任務(wù)
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxx"]];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:[NSURL URLWithString:@"daf.af.af.3422"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
}];
// 3.開始任務(wù)
[uploadTask resume];
2.通過設(shè)置配置實現(xiàn)代理方法的方式
a>. dataTask下載文件
// 1.創(chuàng)建默認配置的NSURLSession對象
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
/*
NSURLSessionConfiguration:session對象的全局配置設(shè)置忘朝,一般使用默認配置就可以
delegate:設(shè)置代理
delegateQueue:代理方法在哪個隊列中執(zhí)行(在哪個線程中調(diào)用),如果是主隊列那么在主線程中執(zhí)行灰署,如果是非主隊列,那么在子線程中執(zhí)行
*/
// 2.創(chuàng)建一個Task, 請求方法為get和post均可
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:@"http://xxxx"]];
// 3.執(zhí)行任務(wù)(可暫停局嘁、取消溉箕、恢復(fù)等)
[dataTask resume]; // [dataTask suspend] [dataTask cancel];
// 4. 遵守代理協(xié)議,實現(xiàn)代理方法(3個相關(guān)的代理方法)
/* 1.當接收到服務(wù)器響應(yīng)的時候調(diào)用 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
// 通過該block回調(diào)悦昵,告訴服務(wù)器端是否接收返回的數(shù)據(jù)
completionHandler(NSURLSessionResponseAllow);
/*
NSURLSessionResponseCancel = 0, 取消任務(wù)
NSURLSessionResponseAllow = 1, 接收任務(wù)
NSURLSessionResponseBecomeDownload = 2, 轉(zhuǎn)變成下載
NSURLSessionResponseBecomeStream NS_ENUM_AVAILABLE(10_11, 9_0) = 3, 轉(zhuǎn)變成流
*/
}
/* 2.當接收到服務(wù)器返回的數(shù)據(jù)時調(diào)用 該方法可能會被調(diào)用多次 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
}
/* 3.當請求完成之后調(diào)用該方法 不論是請求成功還是請求失敗都調(diào)用該方法肴茄,如果請求失敗,那么error對象有值但指,否則那么error對象為空 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
}
b>. downloadTask下載文件
和dataTask一樣只是代理方法有所區(qū)別
// 4. 遵守代理協(xié)議寡痰,實現(xiàn)代理方法(3個相關(guān)的代理方法)
/* 1.可以在該方法中監(jiān)聽文件下載的進度,該方法會被調(diào)用多次
*/
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
}
/* 2.恢復(fù)下載的時候調(diào)用該方法 */
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
}
/* 3.下載完成之后調(diào)用該方法 */
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(nonnull NSURL *)location {
}
/* 4.請求完成之后調(diào)用如果請求失敗棋凳,那么error有值 */
-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error {
}
注意:
- 如果通過[downloadTask cancel]取消任務(wù)拦坠,那么任務(wù)不能恢復(fù)了
- 通過以下方法暫停,那么該方法會以resumeData保存當前文件的已經(jīng)下載數(shù)據(jù)剩岳,任務(wù)可以恢復(fù)下載
[self.downloadTask cancelByProducingResumeData:^(NSData * __nullable resumeData) {
self.resumeData = resumeData;
}];
恢復(fù)下載方法
self.downloadTask=[self.session downloadTaskWithResumeData:self.resumeData];
[self.downloadTask resume];
- 缺點
01 如果用戶點擊暫停之后退出程序贞滨,那么需要把恢復(fù)下載的數(shù)據(jù)寫一份到沙盒,代碼復(fù)雜度更
02 如果用戶在下載中途未保存恢復(fù)下載數(shù)據(jù)即退出程序拍棕,則不具備可操作性
c>. uploadTask上傳文件
//1.創(chuàng)建session
self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//2.創(chuàng)建task
//2.1 創(chuàng)建請求對象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxxxx"]];
//2.2 設(shè)置請求方法
request.HTTPMethod = @"POST";
//2.3.設(shè)置請求頭
NSString *header = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",str];
[request setValue:header forHTTPHeaderField:@"Content-Type"];
//2.4設(shè)置文件上傳的文件內(nèi)容
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromData:data];
[uploadTask resume];
// 4. 遵守代理協(xié)議晓铆,實現(xiàn)代理方法
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
}
后臺會話
當我們的運用退出,如果后臺下載任務(wù)完成或則需要證書時莫湘,后臺自動重啟我們的運用尤蒿,同時調(diào)用UIApplicationDelegate對象application:handlerEventsForBackgroundURLSession:completionHandler:方法,這個方法會提供session的標識幅垮,然后我們要利用session的標識創(chuàng)建后臺配置腰池,繼而創(chuàng)建新的后臺會話,與后臺的activity關(guān)聯(lián)。當后臺下載任務(wù)完成時示弓,會調(diào)用后臺session的代理方法URLSessioinDidFinishEventsForBackgroundURLSession:然后調(diào)用存儲的完成處理
如果在程序掛起時有任何任務(wù)完成讳侨,則會調(diào)用URLSession:downloadTask:didFinishDownloadingToURL:方法。同樣的奏属,如果任務(wù)需要證書跨跨,則NSURLSession對象會在適當?shù)臅r候調(diào)用URLSession:task:didReceiveChallenge:completionHandler: 和URLSession:didReceiveChallenge:completionHandler:方法。
(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler
{
NSURLSessionConfiguration *backgroundConfigObject = [NSURLSessionConfiguration backgroundSessionConfiguration:identifier];
URLSession *sessionDelegate = [[URLSession alloc] init];
NSURLSession *backgroundSession = [NSURLSession sessionWithConfiguration:backgroundConfigObject
delegate:sessionDelegate
delegateQueue:[NSOperationQueue mainQueue]];
[sessionDelegate addCompletionHandler:completionHandler forSession:identifier];
}
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
NSLog(@"background url session %@", session);
if (session.configuration.identifier)
{
[self callCompletionHandlerForSession:session.configuration.identifier];
}
}
- (void)callCompletionHandlerForSession:(NSString *)identifier
{
CompletionHandlerType handler = [self.completionHandlerDictionary objectForKey:identifier];
if (handler) {
[self.completionHandlerDictionary removeObjectForKey:identifier];
handler();
}
}
注意:
NSURLSession對象的釋放
-(void)dealloc {
//在最后的時候應(yīng)該把session釋放囱皿,以免造成內(nèi)存泄露
// NSURLSession設(shè)置過代理后勇婴,需要在最后(比如控制器銷毀的時候)調(diào)用session的invalidateAndCancel或者resetWithCompletionHandler,才不會有內(nèi)存泄露
// [self.session invalidateAndCancel];
[self.session resetWithCompletionHandler:^{
NSLog(@"釋放---");
}];