NSURLSession及相關(guān)的類提供通過HTTP協(xié)議下載數(shù)據(jù)的API此改。該類提供了大量代理方法來支持認(rèn)證和后臺(tái)下載(程序未運(yùn)行或掛起時(shí))功能。
為了使用NSURLSession狼忱,我們的應(yīng)用會(huì)創(chuàng)建一系列的會(huì)話,每個(gè)會(huì)話負(fù)責(zé)協(xié)調(diào)一組相關(guān)數(shù)據(jù)的傳輸任務(wù)他巨。在每個(gè)會(huì)話中澡匪,我們的應(yīng)用添加一系列的任務(wù)唠摹,每個(gè)任務(wù)都表示一個(gè)對(duì)指定URL的請(qǐng)求测蹲。與大多數(shù)網(wǎng)絡(luò)API一樣喜喂,NSURLSession API是異步的勃刨。如果我們使用系統(tǒng)提供的代理饲帅,我們必須提供一個(gè)請(qǐng)求完成處理block侵蒙,以便在請(qǐng)求成功或失敗時(shí)返回?cái)?shù)據(jù)給我們的應(yīng)用也榄。如果我們提供自定義的代理對(duì)象惦费,則任務(wù)對(duì)象調(diào)用這些代理方法趟脂,并回傳從服務(wù)端獲取的數(shù)據(jù)(如果是文件下載泰讽,則當(dāng)傳輸完成時(shí)調(diào)用)。
NSURLSession提供了status和progress屬性昔期,并作為額外的信息傳遞給代理已卸。同時(shí)它支持取消、恢復(fù)硼一、掛起操作累澡,并支持?jǐn)帱c(diǎn)續(xù)傳功能。
要掌握NSURLSession的使用般贼,我們需要了解下URL會(huì)話的一些內(nèi)容
URL會(huì)話
在一個(gè)會(huì)話中的任務(wù)的行為取決于三個(gè)方面:
session的類型(由創(chuàng)建會(huì)話時(shí)的配置對(duì)象確定)
任務(wù)的類型
當(dāng)任務(wù)創(chuàng)建時(shí)應(yīng)用是否在前臺(tái)
NSURLSession支持以下三種會(huì)話類型:
默認(rèn)會(huì)話:行為與其它下載URL的Foundation方法類似愧哟。使用基于磁盤的緩存策略,并在用戶keychain中存儲(chǔ)證書哼蛆。
短暫會(huì)話(Ephemeral sessions):不存儲(chǔ)任何數(shù)據(jù)在磁盤中蕊梧;所有的緩存,證書存儲(chǔ)等都保存在RAM中并與會(huì)話綁定腮介。這樣肥矢,當(dāng)應(yīng)用結(jié)束會(huì)話時(shí),它們被自動(dòng)釋放叠洗。
后臺(tái)會(huì)話(Background sessions):類似于默認(rèn)會(huì)話橄抹,除了有一個(gè)獨(dú)立的進(jìn)程來處理所有的數(shù)據(jù)傳輸靴迫。
在一個(gè)會(huì)話中,NSURLSession支持三種任務(wù)類型
數(shù)據(jù)任務(wù):使用NSData對(duì)象來發(fā)送和接收數(shù)據(jù)楼誓。數(shù)據(jù)任務(wù)可以分片返回?cái)?shù)據(jù)玉锌,也可以通過完成處理器一次性返回?cái)?shù)據(jù)。由于數(shù)據(jù)任務(wù)不存儲(chǔ)數(shù)據(jù)到文件疟羹,所以不支持后臺(tái)會(huì)話.
下載任務(wù):以文件的形式接收數(shù)據(jù)主守,當(dāng)程序不運(yùn)行時(shí)支持后臺(tái)下載
上傳任務(wù):通常以文件的形式發(fā)送數(shù)據(jù),支持后臺(tái)上傳榄融。
NSURLSession支持在程序掛起時(shí)在后臺(tái)傳輸數(shù)據(jù)参淫。后臺(tái)傳輸只由使用后臺(tái)會(huì)話配置對(duì)象創(chuàng)建的會(huì)話提供。使用后臺(tái)會(huì)話時(shí)愧杯,由于實(shí)際傳輸是在一個(gè)獨(dú)立的進(jìn)程中傳輸涎才,且重啟應(yīng)用進(jìn)程相當(dāng)損耗資源,只有少量特性可以使用力九,所以有以下限制:
會(huì)話必須提供事件分發(fā)的代理耍铜。
只支持HTTP和HTTPS協(xié)議
只支持上傳和下載任務(wù)
總是伴隨著重定義操作
如果當(dāng)應(yīng)用在后臺(tái)時(shí)初始化的后臺(tái)傳輸,則配置對(duì)象的discretionary屬性為true
在iOS中跌前,當(dāng)我們的應(yīng)用不再運(yùn)行時(shí)棕兼,如果后臺(tái)下載任務(wù)完成或者需要證書,則系統(tǒng)會(huì)在后臺(tái)自動(dòng)重啟我們的應(yīng)用抵乓,同時(shí)調(diào)用UIApplicationDelegate對(duì)象的application:handlerEventsForBackgroundURLSession:completionHandler:方法伴挚。這個(gè)調(diào)用會(huì)提供啟動(dòng)的應(yīng)用的session的標(biāo)識(shí)。我們的應(yīng)用應(yīng)該存儲(chǔ)完成處理器灾炭,使用相同的標(biāo)識(shí)來創(chuàng)建后臺(tái)配置對(duì)象茎芋,然后使用配置對(duì)象來創(chuàng)建會(huì)話。新的會(huì)話會(huì)與運(yùn)行的后臺(tái)activity關(guān)聯(lián)蜈出。當(dāng)會(huì)話完成后臺(tái)下載任務(wù)時(shí)田弥,會(huì)給會(huì)話代理發(fā)送一個(gè)URLSessioinDidFinishEventsForBackgroundURLSession:消息。我們的代理對(duì)象然后調(diào)用存儲(chǔ)的完成處理器掏缎。
如果在程序掛起時(shí)有任何任務(wù)完成皱蹦,則會(huì)調(diào)用URLSession:downloadTask:didFinishDownloadingToURL:方法煤杀。同樣的眷蜈,如果任務(wù)需要證書,則NSURLSession對(duì)象會(huì)在適當(dāng)?shù)臅r(shí)候調(diào)用URLSession:task:didReceiveChallenge:completionHandler: 和URLSession:didReceiveChallenge:completionHandler:方法沈自。
這里需要注意的是必須為每個(gè)標(biāo)識(shí)創(chuàng)建一個(gè)會(huì)話酌儒,共享相同標(biāo)識(shí)的多個(gè)會(huì)話的行為是未定義的。
會(huì)話和任務(wù)對(duì)象實(shí)現(xiàn)了NSCopying協(xié)議:
當(dāng)應(yīng)用拷貝一個(gè)會(huì)話或任務(wù)對(duì)象時(shí)枯途,會(huì)獲取相同對(duì)象的指針
當(dāng)應(yīng)用拷貝一個(gè)配置對(duì)象時(shí)忌怎,會(huì)獲取一個(gè)可單獨(dú)修改的新的對(duì)象
創(chuàng)建并配置NSURLSession
我們下面舉個(gè)簡單的實(shí)例來說明一個(gè)NSURLSession與服務(wù)端的數(shù)據(jù)交互籍滴。
代碼清單1:聲明三種類型會(huì)話對(duì)象
@interface URLSession : NSObject
@property (nonatomic, strong) NSURLSession *backgroundSession;
@property (nonatomic, strong) NSURLSession *defaultSession;
@property (nonatomic, strong) NSURLSession *ephemeralSession;
@property (nonatomic, strong) NSMutableDictionary *completionHandlerDictionary;
- (void)addCompletionHandler:(CompletionHandlerType)handler forSession:(NSString *)identifier;
- (void)callCompletionHandlerForSession:(NSString *)identifier;
@end
NSURLSession提供了大量的配置選項(xiàng),包括:
支持緩存榴啸、cookie孽惰,認(rèn)證及協(xié)議的私有存儲(chǔ)
認(rèn)證
上傳下載文件
每個(gè)主機(jī)的配置最大數(shù)
超時(shí)時(shí)間
支持的TLS最小最小版本
自定義代理字典
控制cookie策略
控制HTTP管道行為
由于大部分設(shè)置都包含在一個(gè)獨(dú)立的配置對(duì)象中,所以我們可以重用這些配置鸥印。當(dāng)我們初始一個(gè)會(huì)話對(duì)象時(shí)勋功,我們指定了如下內(nèi)容
一個(gè)配置對(duì)象,用于管理其中的會(huì)話和任務(wù)的行為
一個(gè)代理對(duì)象库说,用于在收到數(shù)據(jù)時(shí)處理輸入數(shù)據(jù)狂鞋,及會(huì)話和任務(wù)中的其它事件,如服務(wù)端認(rèn)證潜的、確定一個(gè)資源加載請(qǐng)求是否應(yīng)該轉(zhuǎn)換成下載等骚揍。這個(gè)對(duì)象是可選的。但如果我們需要執(zhí)行后臺(tái)傳輸啰挪,則必須提供自定義代理信不。
在實(shí)例一個(gè)會(huì)話對(duì)象后,我們不能改變改變配置或代理脐供。
代碼清單2演示了如何創(chuàng)建一個(gè)會(huì)話
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
// 配置會(huì)話的緩存
NSString *cachePath = @"/MyCacheDirectory";
NSArray *pathList = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *path = [pathList objectAtIndex:0];
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
NSString *fullCachePath = [[path stringByAppendingPathComponent:bundleIdentifier] stringByAppendingPathComponent:cachePath];
NSLog(@"Cache path: %@", fullCachePath);
NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:16384 diskCapacity:268435456 diskPath:cachePath];
defaultConfigObject.URLCache = cache;
defaultConfigObject.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;
self.defaultSession = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:[NSOperationQueue mainQueue]];
除了后臺(tái)配置對(duì)象外浑塞,我們可以重用會(huì)話的配置對(duì)象來創(chuàng)建新的會(huì)話,正如上面所講的政己,拷貝一個(gè)配置對(duì)象會(huì)生成一個(gè)新的獨(dú)立的配置對(duì)象酌壕。我們可以在任何時(shí)候安全的修改配置對(duì)象。當(dāng)創(chuàng)建一個(gè)會(huì)話時(shí)歇由,會(huì)話會(huì)對(duì)配置對(duì)象進(jìn)行深拷貝卵牍,所以修改只會(huì)影響到新的會(huì)話。代理清單3演示了創(chuàng)建一個(gè)新的會(huì)話沦泌,這個(gè)會(huì)話使用重用的配置對(duì)象糊昙。
代碼清單3:重用會(huì)話對(duì)象
self.ephemeralSession = [NSURLSession sessionWithConfiguration:ephemeralConfigObject delegate:self delegateQueue:[NSOperationQueue mainQueue]];
ephemeralConfigObject.allowsCellularAccess = YES;
// ...
NSURLSession *ephemeralSessionWifiOnly = [NSURLSession sessionWithConfiguration:ephemeralConfigObject delegate:self delegateQueue:[NSOperationQueue mainQueue]];
使用NSURLSession獲取數(shù)據(jù)基本就是兩步:
創(chuàng)建一個(gè)配置對(duì)象及基于這個(gè)對(duì)象的會(huì)話
定義一個(gè)請(qǐng)求完成處理器來處理獲取到的數(shù)據(jù)。
如果使用系統(tǒng)提供的代理谢谦,只需要代碼清單4這幾行代碼即可搞定
代碼清單4:使用系統(tǒng)提供代理
NSURLSession *delegateFreeSession = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:[NSOperationQueue mainQueue]];
[delegateFreeSession dataTaskWithRequest:@"http://www.sina.com"
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"Got response %@", response);
}];
只是系統(tǒng)提供的代理只提供有限的網(wǎng)絡(luò)行為释牺。如果應(yīng)用需要更多的處理,如自定義認(rèn)證或后臺(tái)下載等回挽,則需要使用自定義的代理没咙。使用自定義代理來獲取數(shù)據(jù)時(shí),代理必須實(shí)現(xiàn)以下方法:
URLSession:dataTask:didReceiveData: 從請(qǐng)求提供數(shù)據(jù)給我們的任務(wù)千劈,一次一個(gè)數(shù)據(jù)塊
URLSession:task:didCompleteWithError: 表示任務(wù)已經(jīng)接受了所有的數(shù)據(jù)祭刚。
如果我們?cè)赨RLSession:dataTask:didReceiveData:方法返回后使用數(shù)據(jù),則需要將數(shù)據(jù)存儲(chǔ)在某個(gè)地方。
代碼清單5:演示了一個(gè)數(shù)據(jù)訪問實(shí)例:
NSURL *url = [NSURL URLWithString:@"http://www.sina.com"];
NSURLSessionDataTask *dataTask = [self.defaultSession dataTaskWithURL:url];
[dataTask resume];
如果遠(yuǎn)程服務(wù)器返回的狀態(tài)表示需要一個(gè)認(rèn)證涡驮,且認(rèn)證需要連接級(jí)別的處理時(shí)暗甥,NSURLSession將調(diào)用認(rèn)證相關(guān)代理方法。這個(gè)具體我們后面文章將詳細(xì)討論捉捅。
處理iOS后臺(tái)Activity
在iOS中使用NSURLSession時(shí)撤防,當(dāng)一個(gè)下載完成時(shí),會(huì)自動(dòng)啟動(dòng)我們的應(yīng)用棒口。應(yīng)用的代理方法application:handleEventsForBackgroundURLSession:completionHandler: 負(fù)責(zé)創(chuàng)建一個(gè)合適的會(huì)話即碗,存儲(chǔ)請(qǐng)求完成處理器,并在會(huì)話調(diào)用會(huì)話代理的URLSessionDidFinishEventsForBackgroundURLSession: 方法時(shí)調(diào)用這個(gè)處理器陌凳。代碼清單6與代碼清單7演示了這個(gè)處理流程
代碼清單6:iOS后臺(tái)下載的會(huì)話代理方法
- (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();
}
}
代碼清單7:iOS后臺(tái)下載的App 代理方法
- (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];
}