NSURLSession學習筆記

NSURLSession學習筆記(一)簡介html

1、URL Session的基本概念ios

1.三種工做模式:git

默認會話模式(default):工做模式相似於原來的NSURLConnection,使用的是基於磁盤緩存的持久化策略,使用用戶keychain中保存的證書進行認證受權。github

瞬時會話模式(ephemeral):該模式不使用磁盤保存任何數據。全部和會話相關的caches,證書,cookies等都被保存在RAM中,所以當程序使會話無效,這些緩存的數據就會被自動清空。web

後臺會話模式(background):該模式在後臺完成上傳和下載,在建立Configuration對象的時候須要提供一個NSString類型的ID用於標識完成工做的後臺會話。xcode

 

2.NSURLSession支持的三種任務緩存

NSURLSession類支持三種類型的任務:加載數據,下載和上傳。性能優化

 

2、相關的類cookie

NSURLConnection這個名字,實際上指的是一組構成Foundation框架中URL加載系統的相互關聯的組件:NSURLRequest,NSURLResponse,NSURLProtocol,NSURLCache,NSHTTPCookieStorage,NSURLCredentialStorage,以及和它同名的NSURLConnection。網絡

在WWDC 2013中,Apple的團隊對NSURLConnection進行了重構,並推出了NSURLSession做爲替代。

NSURLSession也是一組相互依賴的類,它的大部分組件和NSURLConnection中的組件相同如NSURLRequest,NSURLCache等。而NSURLSession的不一樣之處在於,它將NSURLConnection替換爲NSURLSession和NSURLSessionConfiguration,以及3個NSURLSessionTask的子類:NSURLSessionDataTask, NSURLSessionUploadTask, 和NSURLSessionDownloadTask。

 

下面來講下NSURLSession新推出的類:

1.NSURLSessionConfiguration類

其中NSURLSessionConfiguration用於配置會話的屬性,能夠經過該類配置會話的工做模式:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. + (NSURLSessionConfiguration *)defaultSessionConfiguration;  
  2. + (NSURLSessionConfiguration *)ephemeralSessionConfiguration;  
  3. + (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier;  


在backgroundSessionConfiguration:方法中的identifier參數指定了會話的ID,用於標記後臺的session。

 

該類的其中兩個屬性:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* allow request to route over cellular. */  
  2. @property BOOL allowsCellularAccess;  
  3.   
  4. /* allows background tasks to be scheduled at the discretion of the system for optimal performance. */  
  5. @property (getter=isDiscretionary) BOOL discretionary NS_AVAILABLE(NA, 7_0);  


allowsCellularAccess 屬性指定是否容許使用蜂窩鏈接, discretionary屬性爲YES時表示當程序在後臺運做時由系統本身選擇最佳的網絡鏈接配置,該屬性能夠節省經過蜂窩鏈接的帶寬。在使用後臺傳輸數據的時候,建議使用discretionary屬性,而不是allowsCellularAccess屬性,由於它會把WiFi和電源可用性考慮在內。補充:這個標誌容許系統爲分配任務進行性能優化。這意味着只有當設備有足夠電量時,設備才經過Wifi進行數據傳輸。若是電量低,或者只僅有一個蜂窩鏈接,傳輸任務是不會運行的。後臺傳輸老是在discretionary模式下運行。

 

 

2.NSURLSession類

獲取NSURLSession類對象有幾種方式:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* 
  2.  * The shared session uses the currently set global NSURLCache, 
  3.  * NSHTTPCookieStorage and NSURLCredentialStorage objects. 
  4.  */  
  5. + (NSURLSession *)sharedSession;  
  6.   
  7. /* 
  8.  * Customization of NSURLSession occurs during creation of a new session. 
  9.  * If you only need to use the convenience routines with custom 
  10.  * configuration options it is not necessary to specify a delegate. 
  11.  * If you do specify a delegate, the delegate will be retained until after 
  12.  * the delegate has been sent the URLSession:didBecomeInvalidWithError: message. 
  13.  */  
  14. + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;  
  15. + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id <NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue;  


第一種方式是使用靜態的sharedSession方法,該類使用共享的會話,該會話使用全局的Cache,Cookie和證書。

 

第二種方式是經過sessionWithConfiguration:方法建立對象,也就是建立對應配置的會話,與NSURLSessionConfiguration合做使用。

第三種方式是經過sessionWithConfiguration:delegate:delegateQueue方法建立對象,二三兩種方式能夠建立一個新會話並定製其會話類型。該方式中指定了session的委託和委託所處的隊列。當再也不須要鏈接時,能夠調用Session的invalidateAndCancel直接關閉,或者調用finishTasksAndInvalidate等待當前Task結束後關閉。這時Delegate會收到URLSession:didBecomeInvalidWithError:這個事件。Delegate收到這個事件以後會被解引用。

 

3.NSURLSessionTask類

NSURLSessionTask是一個抽象子類,它有三個子類:NSURLSessionDataTask,NSURLSessionUploadTask和NSURLSessionDownloadTask。這三個類封裝了現代應用程序的三個基本網絡任務:獲取數據,好比JSON或XML,以及上傳和下載文件。

下面是其繼承關係:

有多種方法建立對應的任務對象:

(1)NSURLSessionDataTask

經過request對象或url建立:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* Creates a data task with the given request.  The request may have a body stream. */  
  2. - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;  
  3.   
  4. /* Creates a data task to retrieve the contents of the given URL. */  
  5. - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;  


經過request對象或url建立,同時指定任務完成後經過completionHandler指定回調的代碼塊:

 

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* 
  2.  * data task convenience methods.  These methods create tasks that 
  3.  * bypass the normal delegate calls for response and data delivery, 
  4.  * and provide a simple cancelable asynchronous interface to receiving 
  5.  * data.  Errors will be returned in the NSURLErrorDomain,  
  6.  * see <Foundation/NSURLError.h>.  The delegate, if any, will still be 
  7.  * called for authentication challenges. 
  8.  */  
  9. - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;  
  10. - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;  


(2)NSURLSessionUploadTask

 

經過request建立,在上傳時指定文件源或數據源。

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* Creates an upload task with the given request.  The body of the request will be created from the file referenced by fileURL */  
  2. - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;  
  3.   
  4. /* Creates an upload task with the given request.  The body of the request is provided from the bodyData. */  
  5. - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;  
  6.   
  7. /* Creates an upload task with the given request.  The previously set body stream of the request (if any) is ignored and the URLSession:task:needNewBodyStream: delegate will be called when the body payload is required. */  
  8. - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;  


在建立upload task對象時,經過completionHandler指定任務完成後的回調代碼塊:

 

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* 
  2.  * upload convenience method. 
  3.  */  
  4. - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;  
  5. - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;  

 

 

(3)NSURLSessionDownloadTask

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* Creates a download task with the given request. */  
  2. - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;  
  3.   
  4. /* Creates a download task to download the contents of the given URL. */  
  5. - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url;  
  6.   
  7. /* Creates a download task with the resume data.  If the download cannot be successfully resumed, URLSession:task:didCompleteWithError: will be called. */  
  8. - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;  


下載任務支持斷點續傳,第三種方式是經過以前已經下載的數據來建立下載任務。
一樣地能夠經過completionHandler指定任務完成後的回調代碼塊:

 

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* 
  2.  * download task convenience methods.  When a download successfully 
  3.  * completes, the NSURL will point to a file that must be read or 
  4.  * copied during the invocation of the completion routine.  The file 
  5.  * will be removed automatically. 
  6.  */  
  7. - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;  
  8. - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;  
  9. - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;  



 

4.NSURLSessionDelegate和NSURLSessionTaskDelegate協議

在協議的方法中能夠完成各類各樣的回調動做,如身份驗證、完成任務後的動做、錯誤處理和後臺任務完成的動做等。委託方法指定在NSURLSession中必定數量的字節傳輸使用int64_t類型的參數。

這裏只說下後臺任務的一個委託方法:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* If an application has received an 
  2.  * -application:handleEventsForBackgroundURLSession:completionHandler: 
  3.  * message, the session delegate will receive this message to indicate 
  4.  * that all messages previously enqueued for this session have been 
  5.  * delivered.  At this time it is safe to invoke the previously stored 
  6.  * completion handler, or to begin any internal updates that will 
  7.  * result in invoking the completion handler. 
  8.  */  
  9. - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session NS_AVAILABLE_IOS(7_0);  

 

 

合做使用的ApplicationDelegate方法:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. // Applications using an NSURLSession with a background configuration may be launched or resumed in the background in order to handle the  
  2. // completion of tasks in that session, or to handle authentication. This method will be called with the identifier of the session needing  
  3. // attention. Once a session has been created from a configuration object with that identifier, the session's delegate will begin receiving  
  4. // callbacks. If such a session has already been created (if the app is being resumed, for instance), then the delegate will start receiving  
  5. // callbacks without any action by the application. You should call the completionHandler as soon as you're finished handling the callbacks.  
  6. - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler NS_AVAILABLE_IOS(7_0);  


將任務切換到後臺以後,Session的Delegate不會再收到和Task相關的消息。當全部Task全都完成後,程序將被喚醒,並調用ApplicationDelegate的application:handleEventsForBackgroundURLSession:completionHandler:回調,在這裏要爲後臺session(由background session的identifier標識)指定對應的回調代碼塊。

 

隨後,對於每個完成的後臺Task調用該Session的Delegate中的URLSession:downloadTask:didFinishDownloadingToURL:(成功的話)和URLSession:task:didCompleteWithError:(成功或者失敗都會調用)方法作處理,以上的回調代碼塊能夠在這裏調用。

 

 

 

NSURLSession學習筆記(二)Session Task

Session Task分爲三種Data Task,Upload Task,Download Task。毫無疑問,Session Task是整個NSURLSession架構的核心目標。

下面寫了一個簡單的Demo來初步使用下三種任務對象。這裏使用的是convenience methods,並無定製session和使用協議,都是採用completionHandler做爲回調動做。


故事板內容爲:

 

第一種Data Task用於加載數據,使用全局的shared session和dataTaskWithRequest:completionHandler:方法建立。代碼以下:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* 使用NSURLSessionDataTask加載網頁數據 */  
  2. - (IBAction)loadData:(id)sender {  
  3.     // 開始加載數據,讓spinner轉起來  
  4.     [self.spinner startAnimating];  
  5.       
  6.     // 建立Data Task,用於打開個人csdn blog主頁  
  7.     NSURL *url = [NSURL URLWithString:@"http://blog.csdn.net/u010962810"];  
  8.     NSURLRequest *request = [NSURLRequest requestWithURL:url];  
  9.     NSURLSession *session = [NSURLSession sharedSession];  
  10.     NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request  
  11.                                                 completionHandler:  
  12.                                       ^(NSData *data, NSURLResponse *response, NSError *error) {  
  13.                                           // 輸出返回的狀態碼,請求成功的話爲200  
  14.                                           [self showResponseCode:response];  
  15.                                             
  16.                                           // 在webView中加載數據  
  17.                                           [self.webView loadData:data  
  18.                                                         MIMEType:@"text/html"  
  19.                                                 textEncodingName:@"utf-8"  
  20.                                                          baseURL:nil];  
  21.                                             
  22.                                           // 加載數據完畢,中止spinner  
  23.                                           [self.spinner stopAnimating];  
  24.                                       }];  
  25.     // 使用resume方法啓動任務  
  26.     [dataTask resume];  
  27. }  
  28.   
  29. /* 輸出http響應的狀態碼 */  
  30. - (void)showResponseCode:(NSURLResponse *)response {  
  31.     NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;  
  32.     NSInteger responseStatusCode = [httpResponse statusCode];  
  33.     NSLog(@"%d", responseStatusCode);  
  34. }  

 

 

completionHandler指定任務完成後的動做。注意必定要使用resume方法啓動任務。(Upload Task和Download Task同理)

運行結果:

 

第二種Upload Task用於完成上傳文件任務,使用方法相似:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* 使用NSURLSessionUploadTask上傳文件 */  
  2. - (IBAction)uploadFile:(id)sender {  
  3. //    NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"];  
  4. //    NSURLRequest *request = [NSURLRequest requestWithURL:URL];  
  5. //    NSData *data = ...;  
  6. //      
  7. //    NSURLSession *session = [NSURLSession sharedSession];  
  8. //    NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request  
  9. //                                                               fromData:data  
  10. //                                                      completionHandler:  
  11. //                                          ^(NSData *data, NSURLResponse *response, NSError *error) {  
  12. //                                              // ...  
  13. //                                          }];  
  14. //      
  15. //    [uploadTask resume];  
  16. }  



 

第三種Download Task用於完成下載文件的任務,使用全局的shared session和downloadTaskWithRequest:completionHandler:方法建立。

注意:在下載任務完成後,下載的文件位於tmp目錄下,由代碼塊中的location指定(不妨輸出看看),咱們必需要在completion handler中將文件放到持久化的目錄下保存。代碼以下:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* 使用NSURLSessionDownloadTask下載文件 */  
  2. - (IBAction)downloadFile:(id)sender {  
  3.     [self.spinner startAnimating];  
  4.       
  5.     NSURL *URL = [NSURL URLWithString:@"http://b.hiphotos.baidu.com/image/w%3D2048/sign=6be5fc5f718da9774e2f812b8469f919/8b13632762d0f703b0faaab00afa513d2697c515.jpg"];  
  6.     NSURLRequest *request = [NSURLRequest requestWithURL:URL];  
  7.     NSURLSession *session = [NSURLSession sharedSession];  
  8.     NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request  
  9.                                                             completionHandler:  
  10.                                               ^(NSURL *location, NSURLResponse *response, NSError *error) {  
  11.                                                   [self showResponseCode:response];  
  12.                                                     
  13.                                                   // 輸出下載文件原來的存放目錄  
  14.                                                   NSLog(@"%@", location);  
  15.                                                     
  16.                                                   // 設置文件的存放目標路徑  
  17.                                                   NSString *documentsPath = [self getDocumentsPath];  
  18.                                                   NSURL *documentsDirectoryURL = [NSURL fileURLWithPath:documentsPath];  
  19.                                                   NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:[[response URL] lastPathComponent]];  
  20.                                                     
  21.                                                   // 若是該路徑下文件已經存在,就要先將其移除,在移動文件  
  22.                                                   NSFileManager *fileManager = [NSFileManager defaultManager];  
  23.                                                   if ([fileManager fileExistsAtPath:[fileURL path] isDirectory:NULL]) {  
  24.                                                       [fileManager removeItemAtURL:fileURL error:NULL];  
  25.                                                   }  
  26.                                                   [fileManager moveItemAtURL:location toURL:fileURL error:NULL];  
  27.                                                     
  28.                                                   // 在webView中加載圖片文件  
  29.                                                   NSURLRequest *showImage_request = [NSURLRequest requestWithURL:fileURL];  
  30.                                                   [self.webView loadRequest:showImage_request];  
  31.                                                     
  32.                                                   [self.spinner stopAnimating];  
  33.                                               }];  
  34.       
  35.     [downloadTask resume];  
  36. }  
  37.   
  38. /* 獲取Documents文件夾的路徑 */  
  39. - (NSString *)getDocumentsPath {  
  40.     NSArray *documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
  41.     NSString *documentsPath = documents[0];  
  42.     return documentsPath;  
  43. }  


運行結果:

 

 

 

這個Demo中沒有爲NSURLSession指定session的delegate,因此沒有使用委託中的方法,功能比較有限,並且也沒有自行定製session的配置,因此只能執行簡單的任務,可是對於加載數據,下載一張圖片等任務已經能夠應付自如。對於建立後臺下載任務,支持斷點續傳的下載任務等將在下一篇文章中分析介紹。

 

 

 

NSURLSession學習筆記(三)Download Task

NSURLSession的Download Task用於完成下載任務,本文介紹如何建立斷點續傳的下載任務和後臺下載任務。

 

咱們直接從分析Demo入手:

故事板以下:

 

只有一個View Controller,用於建立各類下載任務,並將下載後的圖片顯示到視圖上,下載過程當中會更新下載進度。

頭文件代碼以下:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. #import <UIKit/UIKit.h>  
  2.   
  3. @interface ViewController : UIViewController <NSURLSessionDownloadDelegate>  
  4.   
  5. /* NSURLSessions */  
  6. @property (strong, nonatomic)           NSURLSession *currentSession;    // 當前會話  
  7. @property (strong, nonatomic, readonly) NSURLSession *backgroundSession; // 後臺會話  
  8.   
  9. /* 下載任務 */  
  10. @property (strong, nonatomic) NSURLSessionDownloadTask *cancellableTask; // 可取消的下載任務  
  11. @property (strong, nonatomic) NSURLSessionDownloadTask *resumableTask;   // 可恢復的下載任務  
  12. @property (strong, nonatomic) NSURLSessionDownloadTask *backgroundTask;  // 後臺的下載任務  
  13.   
  14. /* 用於可恢復的下載任務的數據 */  
  15. @property (strong, nonatomic) NSData *partialData;  
  16.   
  17. /* 顯示已經下載的圖片 */  
  18. @property (weak, nonatomic) IBOutlet UIImageView *downloadedImageView;  
  19.   
  20. /* 下載進度 */  
  21. @property (weak, nonatomic) IBOutlet UILabel *currentProgress_label;  
  22. @property (weak, nonatomic) IBOutlet UIProgressView *downloadingProgressView;  
  23.   
  24. /* 工具欄上的按鈕 */  
  25. @property (weak, nonatomic) IBOutlet UIBarButtonItem *cancellableDownload_barButtonItem;  
  26. @property (weak, nonatomic) IBOutlet UIBarButtonItem *resumableDownload_barButtonItem;  
  27. @property (weak, nonatomic) IBOutlet UIBarButtonItem *backgroundDownload_barButtonItem;  
  28. @property (weak, nonatomic) IBOutlet UIBarButtonItem *cancelTask_barButtonItem;  
  29.   
  30. - (IBAction)cancellableDownload:(id)sender; // 建立可取消的下載任務  
  31. - (IBAction)resumableDownload:(id)sender;   // 建立可恢復的下載任務  
  32. - (IBAction)backgroundDownload:(id)sender;  // 建立後臺下載任務  
  33. - (IBAction)cancelDownloadTask:(id)sender;  // 取消全部下載任務  
  34.   
  35. @end  



 

1、建立普通的下載任務

這種下載任務是能夠取消的,代碼以下:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. - (IBAction)cancellableDownload:(id)sender {  
  2.     if (!self.cancellableTask) {  
  3.         if (!self.currentSession) {  
  4.             [self createCurrentSession];  
  5.         }  
  6.           
  7.         NSString *imageURLStr = @"http://farm6.staticflickr.com/5505/9824098016_0e28a047c2_b_d.jpg";  
  8.         NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURLStr]];  
  9.         self.cancellableTask = [self.currentSession downloadTaskWithRequest:request];  
  10.           
  11.         [self setDownloadButtonsWithEnabled:NO];  
  12.         self.downloadedImageView.image = nil;  
  13.           
  14.         [self.cancellableTask resume];  
  15.     }  
  16. }  


若是當前的session爲空,首先須要建立一個session(該session使用默認配置模式,其delegate爲本身):

 

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* 建立當前的session */  
  2. - (void)createCurrentSession {  
  3.     NSURLSessionConfiguration *defaultConfig = [NSURLSessionConfiguration defaultSessionConfiguration];  
  4.     self.currentSession = [NSURLSession sessionWithConfiguration:defaultConfig delegate:self delegateQueue:nil];  
  5.     self.currentSession.sessionDescription = kCurrentSession;  
  6. }  

 

 

隨後建立下載任務並啓動。

這種任務是可取消的,即下次下載又從0.0%開始:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. if (self.cancellableTask) {  
  2.     [self.cancellableTask cancel];  
  3.     self.cancellableTask = nil;  
  4. }  



 

2、建立可恢復的下載任務

可恢復的下載任務支持斷點續傳,也就是若是暫停當前任務,在下次再執行任務時,將從以前的下載進度中繼續進行。所以咱們首先須要一個NSData對象來保存已經下載的數據:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* 用於可恢復的下載任務的數據 */  
  2. @property (strong, nonatomic) NSData *partialData;  


執行下載任務時,若是是恢復下載,那麼就使用downloadTaskWithResumeData:方法根據partialData繼續下載。代碼以下:

 

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. - (IBAction)resumableDownload:(id)sender {  
  2.     if (!self.resumableTask) {  
  3.         if (!self.currentSession) {  
  4.             [self createCurrentSession];  
  5.         }  
  6.           
  7.         if (self.partialData) { // 若是是以前被暫停的任務,就從已經保存的數據恢復下載  
  8.             self.resumableTask = [self.currentSession downloadTaskWithResumeData:self.partialData];  
  9.         }  
  10.         else { // 不然建立下載任務  
  11.             NSString *imageURLStr = @"http://farm3.staticflickr.com/2846/9823925914_78cd653ac9_b_d.jpg";  
  12.             NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURLStr]];  
  13.             self.resumableTask = [self.currentSession downloadTaskWithRequest:request];  
  14.         }  
  15.           
  16.         [self setDownloadButtonsWithEnabled:NO];  
  17.         self.downloadedImageView.image = nil;  
  18.           
  19.         [self.resumableTask resume];  
  20.     }  
  21. }  


在取消下載任務時,要將partialData數據保存起來,並且不要調用cancel方法:

 

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. else if (self.resumableTask) {  
  2.     [self.resumableTask cancelByProducingResumeData:^(NSData *resumeData) {  
  3.         // 若是是可恢復的下載任務,應該先將數據保存到partialData中,注意在這裏不要調用cancel方法  
  4.         self.partialData = resumeData;  
  5.         self.resumableTask = nil;  
  6.     }];  
  7. }  


另外在恢復下載時,NSURLSessionDownloadDelegate中的如下方法將被調用:

 

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* 從fileOffset位移處恢復下載任務 */  
  2. - (void)URLSession:(NSURLSession *)session  
  3.       downloadTask:(NSURLSessionDownloadTask *)downloadTask  
  4.  didResumeAtOffset:(int64_t)fileOffset  
  5. expectedTotalBytes:(int64_t)expectedTotalBytes {  
  6.     NSLog(@"NSURLSessionDownloadDelegate: Resume download at %lld", fileOffset);  
  7. }  



 

3、建立後臺下載任務

後臺下載任務,顧名思義,當程序進入後臺後,下載任務依然繼續執行。

首先建立一個後臺session單例,這裏的Session配置使用後臺配置模式,使用backgroundSessinConfiguration:方法配置時應該經過後面的參數爲該後臺進程指定一個標識符,在有多個後臺下載任務時這個標識符就起做用了。

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* 建立一個後臺session單例 */  
  2. - (NSURLSession *)backgroundSession {  
  3.     static NSURLSession *backgroundSess = nil;  
  4.     static dispatch_once_t onceToken;  
  5.     dispatch_once(&onceToken, ^{  
  6.         NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:kBackgroundSessionID];  
  7.         backgroundSess = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];  
  8.         backgroundSess.sessionDescription = kBackgroundSession;  
  9.     });  
  10.       
  11.     return backgroundSess;  
  12. }  


在建立後臺下載任務時,應該使用後臺session建立,而後resume。

 

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. - (IBAction)backgroundDownload:(id)sender {  
  2.     NSString *imageURLStr = @"http://farm3.staticflickr.com/2831/9823890176_82b4165653_b_d.jpg";  
  3.     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURLStr]];  
  4.     self.backgroundTask = [self.backgroundSession downloadTaskWithRequest:request];  
  5.       
  6.     [self setDownloadButtonsWithEnabled:NO];  
  7.     self.downloadedImageView.image = nil;  
  8.       
  9.     [self.backgroundTask resume];  
  10. }  


在程序進入後臺後,若是下載任務完成,程序委託中的對應方法將被回調:

 

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* 後臺下載任務完成後,程序被喚醒,該方法將被調用 */  
  2. - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {  
  3.     NSLog(@"Application Delegate: Background download task finished");  
  4.       
  5.     // 設置回調的完成代碼塊  
  6.     self.backgroundURLSessionCompletionHandler = completionHandler;  
  7. }  


而後調用NSURLSessionDownloadDelegate中的方法:

 

如下是

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL*)location中的方法,該方法只有下載成功才被調用:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. else if (session == self.backgroundSession) {  
  2.     self.backgroundTask = nil;  
  3.     AppDelegate *appDelegate = [AppDelegate sharedDelegate];  
  4.     if (appDelegate.backgroundURLSessionCompletionHandler) {  
  5.         // 執行回調代碼塊  
  6.         void (^handler)() = appDelegate.backgroundURLSessionCompletionHandler;  
  7.         appDelegate.backgroundURLSessionCompletionHandler = nil;  
  8.         handler();  
  9.     }  
  10. }  


另外不管下載成功與否,如下方法都會被調用:

 

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* 完成下載任務,不管下載成功仍是失敗都調用該方法 */  
  2. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {  
  3.     NSLog(@"NSURLSessionDownloadDelegate: Complete task");  
  4.       
  5.     dispatch_async(dispatch_get_main_queue(), ^{  
  6.         [self setDownloadButtonsWithEnabled:YES];  
  7.     });  
  8.       
  9.     if (error) {  
  10.         NSLog(@"下載失敗:%@", error);  
  11.         [self setDownloadProgress:0.0];  
  12.         self.downloadedImageView.image = nil;  
  13.     }  
  14. }  


取消後臺下載任務時直接cancel便可:

 

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. else if (self.backgroundTask) {  
  2.     [self.backgroundTask cancel];  
  3.     self.backgroundTask = nil;  
  4. }  



 

4、NSURLSessionDownloadDelegate

爲了實現下載進度的顯示,須要在委託中的如下方法中實現:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* 執行下載任務時有數據寫入 */  
  2. - (void)URLSession:(NSURLSession *)session  
  3.       downloadTask:(NSURLSessionDownloadTask *)downloadTask  
  4.       didWriteData:(int64_t)bytesWritten // 每次寫入的data字節數  
  5.  totalBytesWritten:(int64_t)totalBytesWritten // 當前一共寫入的data字節數  
  6. totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite // 指望收到的全部data字節數  
  7. {  
  8.     // 計算當前下載進度並更新視圖  
  9.     double downloadProgress = totalBytesWritten / (double)totalBytesExpectedToWrite;  
  10.     [self setDownloadProgress:downloadProgress];  
  11. }  
  12.   
  13. /* 根據下載進度更新視圖 */  
  14. - (void)setDownloadProgress:(double)progress {  
  15.     NSString *progressStr = [NSString stringWithFormat:@"%.1f", progress * 100];  
  16.     progressStr = [progressStr stringByAppendingString:@"%"];  
  17.       
  18.     dispatch_async(dispatch_get_main_queue(), ^{  
  19.         self.downloadingProgressView.progress = progress;  
  20.         self.currentProgress_label.text = progressStr;  
  21.     });  
  22. }  



 

從已經保存的數據中恢復下載任務的委託方法,fileOffset指定了恢復下載時的文件位移字節數:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* Sent when a download has been resumed. If a download failed with an 
  2.  * error, the -userInfo dictionary of the error will contain an 
  3.  * NSURLSessionDownloadTaskResumeData key, whose value is the resume 
  4.  * data.  
  5.  */  
  6. - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask  
  7.                                       didResumeAtOffset:(int64_t)fileOffset  
  8.                                      expectedTotalBytes:(int64_t)expectedTotalBytes;  



 

只有下載成功才調用的委託方法,在該方法中應該將下載成功後的文件移動到咱們想要的目標路徑:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* Sent when a download task that has completed a download.  The delegate should  
  2.  * copy or move the file at the given location to a new location as it will be  
  3.  * removed when the delegate message returns. URLSession:task:didCompleteWithError: will 
  4.  * still be called. 
  5.  */  
  6. - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask  
  7.                               didFinishDownloadingToURL:(NSURL *)location;  



 

不管下載成功或失敗都會調用的方法,相似於try-catch-finally中的finally語句塊的執行。若是下載成功,那麼error參數的值爲nil,不然下載失敗,能夠經過該參數查看出錯信息:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. /* Sent as the last message related to a specific task.  Error may be 
  2.  * nil, which implies that no error occurred and this task is complete.  
  3.  */  
  4. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task  
  5.                            didCompleteWithError:(NSError *)error;  



 

 

後臺下載的運行結果:

啓動任務後,進入後臺:


下載完成後,控制檯將會「通知」咱們:

 

[objc]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  1. 2014-02-05 18:30:39.767 DownloadTask[3472:70b] Application Delegate: App did become active  
  2. 2014-02-05 18:30:43.734 DownloadTask[3472:70b] Application Delegate: App will resign active  
  3. 2014-02-05 18:30:43.735 DownloadTask[3472:70b] Application Delegate: App did enter background  
  4. 2014-02-05 18:30:45.282 DownloadTask[3472:70b] Application Delegate: Background download task finished  
  5. 2014-02-05 18:30:45.285 DownloadTask[3472:4907] NSURLSessionDownloadDelegate: Finish downloading  
  6. 2014-02-05 18:30:45.301 DownloadTask[3472:4907] NSURLSessionDownloadDelegate: Complete task  


再次啓動程序,能夠看到加載好的頁面:

 


能夠看到,經過後臺下載讓咱們的程序更加異步地運行。NSURLSession封裝了對應的接口,讓咱們要執行的任務更加專門化,這個新的網絡架構的功能真的很強大。

 

本文的Demo基於https://github.com/ShinobiControls/iOS7-day-by-day改寫,內容基本一致。

原來的Demo也有一篇博客對應:iOS7 Day-by-Day :: Day 1 :: NSURLSession

 

本文的Demo也已經上傳,有興趣的話能夠下載看看。

相關文章
相關標籤/搜索