分類:版權聲明:本文為博主原創文章,未經博主允許不得
NSURLSession是iOS7中新的網絡接口,它與咱們熟悉的NSURLConnection是并列的。在程序在前臺時,NSURLSession與NSURLConnection可以互為替代工作。注意,如果用戶強制將程序關閉,NSURLSession會斷掉。
NSURLSession提供的功能:1.通過URL將數據下載到內存2.通過URL將數據下載到文件系統3.將數據上傳到指定URL4.在后臺完成上述功能 工作流程如果我們需要利用NSURLSession進行數據傳輸我們需要:1.創建一個NSURLSessionConfiguration,用于第二步創建NSSession時設置工作模式和網絡設置:工作模式分為:一般模式(default):工作模式類似于原來的NSURLConnection,可以使用緩存的Cache,Cookie,鑒權。及時模式(ephemeral):不使用緩存的Cache,Cookie,鑒權。后臺模式(background):在后臺完成上傳下載,創建Configuration對象的時候需要給一個NSString的ID用于追蹤完成工作的Session是哪一個(后面會講到)。 網絡設置:參考NSURLConnection中的設置項。1. 創建一個NSURLSession,系統提供了兩個創建方法:sessionWithConfiguration:sessionWithConfiguration:delegate:delegateQueue: 第一個粒度較低就是根據剛才創建的Configuration創建一個Session,系統默認創建一個新的OperationQueue處理Session的消息。 第二個粒度比較高,可以設定回調的delegate(注意這個回調delegate會被強引用),并且可以設定delegate在哪個OperationQueue回調,如果我們將其設置為[NSOperationQueue mainQueue]就能在主線程進行回調非常的方便。 2.創建一個NSURLRequest調用剛才的NSURLSession對象提供的Task函數,創建一個NSURLSessionTask。 根據職能不同Task有三種子類:NSURLSessionUploadTask:上傳用的Task,傳完以后不會再下載返回結果;NSURLSessionDownloadTask:下載用的Task;NSURLSessionDataTask:可以上傳內容,上傳完成后再進行下載。 得到的Task,調用resume開始工作。 3.如果是細粒度的Session調用,Session與Delegate會在指定的OperationQueue中進行交互,以咱們下載例子,交互過程的順序圖如下(假如不需要鑒權,即非HTTPS請求):
4.當不再需要連接調用Session的invalidateAndCancel直接關閉,或者調用finishTasksAndInvalidate等待當前Task結束后關閉。這時Delegate會收到URLSession:didBecomeInvalidWithError:這個事件。Delegate收到這個事件之后會被解引用。 5.如果是一個BackgroundSession,在Task執行的時候,用戶切到后臺,Session會和applicationDelegate做交互。當程序切到后臺后,在BackgroundSession中的Task還會繼續下載,這部分文檔敘述比較少,現在分三個場景分析下Session和Application的關系: 1)當加入了多個Task,程序沒有切換到后臺。這種情況Task會按照NSURLSessionConfiguration的設置正常下載,不會和ApplicationDelegate有交互。 2)當加入了多個Task,程序切到后臺,所有Task都完成下載。 在切到后臺之后,Session的Delegate不會再收到,Task相關的消息,直到所有Task全都完成后,系統會調用ApplicationDelegate的application:handleEventsForBackgroundURLSession:completionHandler:回調,之后“匯報”下載工作,對于每一個后臺下載的Task調用Session的Delegate中的URLSession:downloadTask:didFinishDownloadingToURL:(成功的話)和URLSession:task:didCompleteWithError:(成功或者失敗都會調用)。 之后調用Session的Delegate回調URLSessionDidFinishEventsForBackgroundURLSession:。 注意:在ApplicationDelegate被喚醒后,會有個參數ComplietionHandler,這個參數是個Block,這個參數要在后面Session的Delegate中didFinish的時候調用一下,如下:@implementation APLAppDelegate - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler { BLog(); /* Store the completion handler. The completion handler is invoked by the view controller's checkForAllDownloadsHavingCompleted method (if all the download tasks have been completed). */ self.backgroundSessionCompletionHandler = completionHandler; } //…… @end //Session的Delegate @implementation APLViewController - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { APLAppDelegate *appDelegate = (APLAppDelegate *)[[UIApplication sharedApplication] delegate]; if (appDelegate.backgroundSessionCompletionHandler) { void (^completionHandler)() = appDelegate.backgroundSessionCompletionHandler; appDelegate.backgroundSessionCompletionHandler = nil; completionHandler(); } NSLog(@"All tasks are finished"); } @end 3)當加入了多個Task,程序切到后臺,下載完成了幾個Task,然后用戶又切換到前臺。(程序沒有退出) 切到后臺之后,Session的Delegate仍然收不到消息。在下載完成幾個Task之后再切換到前臺,系統會先匯報已經下載完成的Task的情況,然后繼續下載沒有下載完成的Task,后面的過程同第一種情況。 4)當加入了多個Task,程序切到后臺,幾個Task已經完成,但還有Task還沒有下載完的時候關掉強制退出程序,然后再進入程序的時候。(程序退出了) 最后這個情況比較有意思,由于程序已經退出了,后面沒有下完Session就不在了后面的Task肯定是失敗了。但是已經下載成功的那些Task,新啟動的程序也沒有聽“匯報”的機會了。經過實驗發現,這個時候之前在NSURLSessionConfiguration設置的NSString類型的ID起作用了,當ID相同的時候,一旦生成Session對象并設置Delegate,馬上可以收到上一次關閉程序之前沒有匯報工作的Task的結束情況(成功或者失敗)。但是當ID不相同,這些情況就收不到了,因此為了不讓自己的消息被別的應用程序收到,或者收到別的應用程序的消息,起見ID還是和程序的Bundle名稱綁定上比較好,至少保證唯一性。 總結就像前面說的,在普通的應用場景下NSURLSession與NSURLConnection相比沒有什么優勢,但是在程序切換到后臺之后Background的Session就顯得更加靈活了。 另外,現在主流的網絡開發框架AFNetworking已經更新到了2.0(只支持iOS 6 / iOS 7),其中最重要的一個更新就是添加了NSURLSession相關的支持。雖然就我現在(2013.10.13)看到他們的源碼中,還沒有完全的支持后臺的Session(或者說沒有考慮全我上述的后臺情況),但是大家有興趣可以關注一下他們后續的更新情況。[objc] view plain copy////////////////////// 代碼演示 01.URLSession 上傳,注意代理是 NSURLSessionTaskDelegate[objc] view plain copy// // MJViewController.m // 01.URLSession 上傳 // // Created by apple on 14-4-30. // Copyright (c) 2014年 itcast. All rights reserved. // #import "MJViewController.h" @interface MJViewController () <NSURLSessionTaskDelegate> @end @implementation MJViewController - (void)viewDidLoad { [super viewDidLoad]; [self uploadFile1]; } #PRagma mark - 監控上傳進度 - (void)uploadFile1 { // 1. URL NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"head8.png" withExtension:nil]; NSURL *url = [NSURL URLWithString:@"http://localhost/uploads/1.png"]; // 2. Request NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:2.0f]; // 1> PUT方法 // PUT // 1) 文件大小無限制 // 2) 可以覆蓋文件 // POST // 1) 通常有限制2M // 2) 新建文件,不能重名 request.HTTPMethod = @"PUT"; // 2> 安全認證 // admin:123456 // result base64編碼 // Basic result /** BASE 64是網絡傳輸中最常用的編碼格式 - 用來將二進制的數據編碼成字符串的編碼方式 BASE 64的用法: 1> 能夠編碼,能夠解碼 2> 被很多的加密算法作為基礎算法 */ NSString *authStr = @"admin:123456"; NSData *authData = [authStr dataUsingEncoding:NSUTF8StringEncoding]; NSString *base64Str = [authData base64EncodedStringWithOptions:0]; NSString *resultStr = [NSString stringWithFormat:@"Basic %@", base64Str]; [request setValue:resultStr forHTTPHeaderField:@"Authorization"]; // 3. Session,全局單例(我們能夠給全局的session設置代理嗎?如果不能為什么?) // sharedSession是全局共享的,因此如果要設置代理,需要單獨實例化一個Session /** NSURLSessionConfiguration(會話配置) defaultSessionConfiguration; // 磁盤緩存,適用于大的文件上傳下載 ephemeralSessionConfiguration; // 內存緩存,以用于小的文件交互,GET一個頭像 backgroundSessionConfiguration:(NSString *)identifier; // 后臺上傳和下載 */ NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[[NSOperationQueue alloc]init]]; // 需要監聽任務的執行狀態 NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromFile:fileURL]; // 4. resume [task resume]; } #pragma mark - 上傳進度的代理方法 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { // bytesSent totalBytesSent totalBytesExpectedToSend // 發送字節(本次發送的字節數) 總發送字節數(已經上傳的字節數) 總希望要發送的字節(文件大小) NSLog(@"%lld-%lld-%lld-", bytesSent, totalBytesSent, totalBytesExpectedToSend); // 已經上傳的百分比 float progress = (float)totalBytesSent / totalBytesExpectedToSend; NSLog(@"%f", progress); } #pragma mark - 上傳完成的代理方法 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { NSLog(@"完成 %@", [NSThread currentThread]); } @end 02.Session下載[objc] view plain copy// // MJViewController.m // 02.Session下載 // // Created by apple on 14-4-30. // Copyright (c) 2014年 itcast. All rights reserved. // #import "MJViewController.h" @interface MJViewController () <NSURLSessionDownloadDelegate> @property (weak, nonatomic) IBOutlet UIImageView *imageView; @end /** // 下載進度跟進 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite; didWriteData totalBytesWritten totalBytesExpectedToWrite 本次寫入的字節數 已經寫入的字節數 預期下載的文件大小 // 完成下載 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location; */ @implementation MJViewController - (void)viewDidLoad { [super viewDidLoad]; [self downloadTask]; } #pragma mark - 下載(GET) - (void)downloadTask { // 1. URL NSURL *url = [NSURL URLWithString:@"http://localhost/itcast/images/head1.png"]; // 2. Request NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:2.0]; // 3. Session NSURLSession *session = [NSURLSession sharedSession]; // 4. download [[session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { // 下載的位置,沙盒中tmp目錄中的臨時文件,會被及時刪除 NSLog(@"下載完成 %@ %@", location, [NSThread currentThread]); /** document 備份,下載的文件不能放在此文件夾中 cache 緩存的,不備份,重新啟動不會被清空,如果緩存內容過多,可以考慮新建一條線程檢查緩存目錄中的文件大小,自動清理緩存,給用戶節省控件 tmp 臨時,不備份,不緩存,重新啟動iphone,會自動清空 */ // 直接通過文件名就可以加載圖像,圖像會常駐內存,具體的銷毀有系統負責 // [UIImage imageNamed:@""]; dispatch_async(dispatch_get_main_queue(), ^{ // 從網絡下載下來的是二進制數據 NSData *data = [NSData dataWithContentsOfURL:location]; // 這種方式的圖像會自動釋放,不占據內存,也不需要放在臨時文件夾中緩存 // 如果用戶需要,可以提供一個功能,保存到用戶的相冊即可 UIImage *image = [UIImage imageWithData:data]; self.imageView.image = image; }); }] resume]; // [task resume]; } @end http://www.cocoachina.com/applenews/devnews/2013/1106/7304.html
新聞熱點
疑難解答