NSURLSession学习笔记(一)简介

一、URL Session的基本概念

1.三种工作模式:

默认会话模式(default):工作模式类似于原来的NSURLConnection,使用的是基于磁盘缓存的持久化策略,使用用户keychain中保存的证书进行认证授权。

瞬时会话模式(ephemeral):该模式不使用磁盘保存任何数据。所有和会话相关的caches,证书,cookies等都被保存在RAM中,因此当程序使会话无效,这些缓存的数据就会被自动清空。

后台会话模式(background):该模式在后台完成上传和下载,在创建Configuration对象的时候需要提供一个NSString类型的ID用于标识完成工作的后台会话。

2.NSURLSession支持的三种任务

NSURLSession类支持三种类型的任务:加载数据,下载和上传。

二、相关的类

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用于配置会话的属性,可以通过该类配置会话的工作模式:

  1. + (NSURLSessionConfiguration *)defaultSessionConfiguration;
  2. + (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
  3. + (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier;

在backgroundSessionConfiguration:方法中的identifier参数指定了会话的ID,用于标记后台的session。

该类的其中两个属性:

  1. /* allow request to route over cellular. */
  2. @property BOOL allowsCellularAccess;
  3. /* allows background tasks to be scheduled at the discretion of the system for optimal performance. */
  4. @property (getter=isDiscretionary) BOOL discretionary NS_AVAILABLE(NA, 7_0);

allowsCellularAccess 属性指定是否允许使用蜂窝连接, discretionary属性为YES时表示当程序在后台运作时由系统自己选择最佳的网络连接配置,该属性可以节省通过蜂窝连接的带宽。在使用后台传输数据的时候,建议使用discretionary属性,而不是allowsCellularAccess属性,因为它会把WiFi和电源可用性考虑在内。补充:这个标志允许系统为分配任务进行性能优化。这意味着只有当设备有足够电量时,设备才通过Wifi进行数据传输。如果电量低,或者只仅有一个蜂窝连接,传输任务是不会运行的。后台传输总是在discretionary模式下运行。

2.NSURLSession类

获取NSURLSession类对象有几种方式:

  1. /*
  2. * The shared session uses the currently set global NSURLCache,
  3. * NSHTTPCookieStorage and NSURLCredentialStorage objects.
  4. */
  5. + (NSURLSession *)sharedSession;
  6. /*
  7. * Customization of NSURLSession occurs during creation of a new session.
  8. * If you only need to use the convenience routines with custom
  9. * configuration options it is not necessary to specify a delegate.
  10. * If you do specify a delegate, the delegate will be retained until after
  11. * the delegate has been sent the URLSession:didBecomeInvalidWithError: message.
  12. */
  13. + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
  14. + (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创建:

  1. /* Creates a data task with the given request.  The request may have a body stream. */
  2. - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;
  3. /* Creates a data task to retrieve the contents of the given URL. */
  4. - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;

通过request对象或url创建,同时指定任务完成后通过completionHandler指定回调的代码块:

  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创建,在上传时指定文件源或数据源。

  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. /* Creates an upload task with the given request.  The body of the request is provided from the bodyData. */
  4. - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;
  5. /* 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. */
  6. - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;

在创建upload task对象时,通过completionHandler指定任务完成后的回调代码块:

  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

  1. /* Creates a download task with the given request. */
  2. - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;
  3. /* Creates a download task to download the contents of the given URL. */
  4. - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url;
  5. /* Creates a download task with the resume data.  If the download cannot be successfully resumed, URLSession:task:didCompleteWithError: will be called. */
  6. - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;

下载任务支持断点续传,第三种方式是通过之前已经下载的数据来创建下载任务。
同样地可以通过completionHandler指定任务完成后的回调代码块:

  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类型的参数。

这里只说下后台任务的一个委托方法:

  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方法:

  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:方法创建。代码如下:

  1. /* 使用NSURLSessionDataTask加载网页数据 */
  2. - (IBAction)loadData:(id)sender {
  3. // 开始加载数据,让spinner转起来
  4. [self.spinner startAnimating];
  5. // 创建Data Task,用于打开我的csdn blog主页
  6. NSURL *url = [NSURL URLWithString:@"http://blog.csdn.net/u010962810"];
  7. NSURLRequest *request = [NSURLRequest requestWithURL:url];
  8. NSURLSession *session = [NSURLSession sharedSession];
  9. NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
  10. completionHandler:
  11. ^(NSData *data, NSURLResponse *response, NSError *error) {
  12. // 输出返回的状态码,请求成功的话为200
  13. [self showResponseCode:response];
  14. // 在webView中加载数据
  15. [self.webView loadData:data
  16. MIMEType:@"text/html"
  17. textEncodingName:@"utf-8"
  18. baseURL:nil];
  19. // 加载数据完毕,停止spinner
  20. [self.spinner stopAnimating];
  21. }];
  22. // 使用resume方法启动任务
  23. [dataTask resume];
  24. }
  25. /* 输出http响应的状态码 */
  26. - (void)showResponseCode:(NSURLResponse *)response {
  27. NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
  28. NSInteger responseStatusCode = [httpResponse statusCode];
  29. NSLog(@"%d", responseStatusCode);
  30. }

completionHandler指定任务完成后的动作。注意一定要使用resume方法启动任务。(Upload Task和Download Task同理)

运行结果:

第二种Upload Task用于完成上传文件任务,使用方法类似:

  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中将文件放到持久化的目录下保存。代码如下:

  1. /* 使用NSURLSessionDownloadTask下载文件 */
  2. - (IBAction)downloadFile:(id)sender {
  3. [self.spinner startAnimating];
  4. NSURL *URL = [NSURL URLWithString:@"http://b.hiphotos.baidu.com/image/w%3D2048/sign=6be5fc5f718da9774e2f812b8469f919/8b13632762d0f703b0faaab00afa513d2697c515.jpg"];
  5. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  6. NSURLSession *session = [NSURLSession sharedSession];
  7. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request
  8. completionHandler:
  9. ^(NSURL *location, NSURLResponse *response, NSError *error) {
  10. [self showResponseCode:response];
  11. // 输出下载文件原来的存放目录
  12. NSLog(@"%@", location);
  13. // 设置文件的存放目标路径
  14. NSString *documentsPath = [self getDocumentsPath];
  15. NSURL *documentsDirectoryURL = [NSURL fileURLWithPath:documentsPath];
  16. NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:[[response URL] lastPathComponent]];
  17. // 如果该路径下文件已经存在,就要先将其移除,在移动文件
  18. NSFileManager *fileManager = [NSFileManager defaultManager];
  19. if ([fileManager fileExistsAtPath:[fileURL path] isDirectory:NULL]) {
  20. [fileManager removeItemAtURL:fileURL error:NULL];
  21. }
  22. [fileManager moveItemAtURL:location toURL:fileURL error:NULL];
  23. // 在webView中加载图片文件
  24. NSURLRequest *showImage_request = [NSURLRequest requestWithURL:fileURL];
  25. [self.webView loadRequest:showImage_request];
  26. [self.spinner stopAnimating];
  27. }];
  28. [downloadTask resume];
  29. }
  30. /* 获取Documents文件夹的路径 */
  31. - (NSString *)getDocumentsPath {
  32. NSArray *documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  33. NSString *documentsPath = documents[0];
  34. return documentsPath;
  35. }

运行结果:

这个Demo中没有为NSURLSession指定session的delegate,所以没有使用委托中的方法,功能比较有限,而且也没有自行定制session的配置,所以只能执行简单的任务,但是对于加载数据,下载一张图片等任务已经可以应付自如。对于创建后台下载任务,支持断点续传的下载任务等将在下一篇文章中分析介绍。

NSURLSession学习笔记(三)Download Task

NSURLSession的Download Task用于完成下载任务,本文介绍如何创建断点续传的下载任务和后台下载任务。

我们直接从分析Demo入手:

故事板如下:

只有一个View Controller,用于创建各种下载任务,并将下载后的图片显示到视图上,下载过程中会更新下载进度。

头文件代码如下:

  1. #import <UIKit/UIKit.h>
  2. @interface ViewController : UIViewController <NSURLSessionDownloadDelegate>
  3. /* NSURLSessions */
  4. @property (strong, nonatomic)           NSURLSession *currentSession;    // 当前会话
  5. @property (strong, nonatomic, readonly) NSURLSession *backgroundSession; // 后台会话
  6. /* 下载任务 */
  7. @property (strong, nonatomic) NSURLSessionDownloadTask *cancellableTask; // 可取消的下载任务
  8. @property (strong, nonatomic) NSURLSessionDownloadTask *resumableTask;   // 可恢复的下载任务
  9. @property (strong, nonatomic) NSURLSessionDownloadTask *backgroundTask;  // 后台的下载任务
  10. /* 用于可恢复的下载任务的数据 */
  11. @property (strong, nonatomic) NSData *partialData;
  12. /* 显示已经下载的图片 */
  13. @property (weak, nonatomic) IBOutlet UIImageView *downloadedImageView;
  14. /* 下载进度 */
  15. @property (weak, nonatomic) IBOutlet UILabel *currentProgress_label;
  16. @property (weak, nonatomic) IBOutlet UIProgressView *downloadingProgressView;
  17. /* 工具栏上的按钮 */
  18. @property (weak, nonatomic) IBOutlet UIBarButtonItem *cancellableDownload_barButtonItem;
  19. @property (weak, nonatomic) IBOutlet UIBarButtonItem *resumableDownload_barButtonItem;
  20. @property (weak, nonatomic) IBOutlet UIBarButtonItem *backgroundDownload_barButtonItem;
  21. @property (weak, nonatomic) IBOutlet UIBarButtonItem *cancelTask_barButtonItem;
  22. - (IBAction)cancellableDownload:(id)sender; // 创建可取消的下载任务
  23. - (IBAction)resumableDownload:(id)sender;   // 创建可恢复的下载任务
  24. - (IBAction)backgroundDownload:(id)sender;  // 创建后台下载任务
  25. - (IBAction)cancelDownloadTask:(id)sender;  // 取消所有下载任务
  26. @end

一、创建普通的下载任务

这种下载任务是可以取消的,代码如下:

  1. - (IBAction)cancellableDownload:(id)sender {
  2. if (!self.cancellableTask) {
  3. if (!self.currentSession) {
  4. [self createCurrentSession];
  5. }
  6. NSString *imageURLStr = @"http://farm6.staticflickr.com/5505/9824098016_0e28a047c2_b_d.jpg";
  7. NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURLStr]];
  8. self.cancellableTask = [self.currentSession downloadTaskWithRequest:request];
  9. [self setDownloadButtonsWithEnabled:NO];
  10. self.downloadedImageView.image = nil;
  11. [self.cancellableTask resume];
  12. }
  13. }

如果当前的session为空,首先需要创建一个session(该session使用默认配置模式,其delegate为自己):

  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%开始:

  1. if (self.cancellableTask) {
  2. [self.cancellableTask cancel];
  3. self.cancellableTask = nil;
  4. }

二、创建可恢复的下载任务

可恢复的下载任务支持断点续传,也就是如果暂停当前任务,在下次再执行任务时,将从之前的下载进度中继续进行。因此我们首先需要一个NSData对象来保存已经下载的数据:

  1. /* 用于可恢复的下载任务的数据 */
  2. @property (strong, nonatomic) NSData *partialData;

执行下载任务时,如果是恢复下载,那么就使用downloadTaskWithResumeData:方法根据partialData继续下载。代码如下:

  1. - (IBAction)resumableDownload:(id)sender {
  2. if (!self.resumableTask) {
  3. if (!self.currentSession) {
  4. [self createCurrentSession];
  5. }
  6. if (self.partialData) { // 如果是之前被暂停的任务,就从已经保存的数据恢复下载
  7. self.resumableTask = [self.currentSession downloadTaskWithResumeData:self.partialData];
  8. }
  9. else { // 否则创建下载任务
  10. NSString *imageURLStr = @"http://farm3.staticflickr.com/2846/9823925914_78cd653ac9_b_d.jpg";
  11. NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURLStr]];
  12. self.resumableTask = [self.currentSession downloadTaskWithRequest:request];
  13. }
  14. [self setDownloadButtonsWithEnabled:NO];
  15. self.downloadedImageView.image = nil;
  16. [self.resumableTask resume];
  17. }
  18. }

在取消下载任务时,要将partialData数据保存起来,而且不要调用cancel方法:

  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中的以下方法将被调用:

  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. }

三、创建后台下载任务

后台下载任务,顾名思义,当程序进入后台后,下载任务依然继续执行。

首先创建一个后台session单例,这里的Session配置使用后台配置模式,使用backgroundSessinConfiguration:方法配置时应该通过后面的参数为该后台进程指定一个标识符,在有多个后台下载任务时这个标识符就起作用了。

  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. return backgroundSess;
  11. }

在创建后台下载任务时,应该使用后台session创建,然后resume。

  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. [self setDownloadButtonsWithEnabled:NO];
  6. self.downloadedImageView.image = nil;
  7. [self.backgroundTask resume];
  8. }

在程序进入后台后,如果下载任务完成,程序委托中的对应方法将被回调:

  1. /* 后台下载任务完成后,程序被唤醒,该方法将被调用 */
  2. - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {
  3. NSLog(@"Application Delegate: Background download task finished");
  4. // 设置回调的完成代码块
  5. self.backgroundURLSessionCompletionHandler = completionHandler;
  6. }

然后调用NSURLSessionDownloadDelegate中的方法:

以下是

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

  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. }

另外无论下载成功与否,以下方法都会被调用:

  1. /* 完成下载任务,无论下载成功还是失败都调用该方法 */
  2. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
  3. NSLog(@"NSURLSessionDownloadDelegate: Complete task");
  4. dispatch_async(dispatch_get_main_queue(), ^{
  5. [self setDownloadButtonsWithEnabled:YES];
  6. });
  7. if (error) {
  8. NSLog(@"下载失败:%@", error);
  9. [self setDownloadProgress:0.0];
  10. self.downloadedImageView.image = nil;
  11. }
  12. }

取消后台下载任务时直接cancel即可:

  1. else if (self.backgroundTask) {
  2. [self.backgroundTask cancel];
  3. self.backgroundTask = nil;
  4. }

四、NSURLSessionDownloadDelegate

为了实现下载进度的显示,需要在委托中的以下方法中实现:

  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. - (void)setDownloadProgress:(double)progress {
  14. NSString *progressStr = [NSString stringWithFormat:@"%.1f", progress * 100];
  15. progressStr = [progressStr stringByAppendingString:@"%"];
  16. dispatch_async(dispatch_get_main_queue(), ^{
  17. self.downloadingProgressView.progress = progress;
  18. self.currentProgress_label.text = progressStr;
  19. });
  20. }

从已经保存的数据中恢复下载任务的委托方法,fileOffset指定了恢复下载时的文件位移字节数:

  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;

只有下载成功才调用的委托方法,在该方法中应该将下载成功后的文件移动到我们想要的目标路径:

  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,否则下载失败,可以通过该参数查看出错信息:

  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;

后台下载的运行结果:

启动任务后,进入后台:

下载完成后,控制台将会“通知”我们:

  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也已经上传,有兴趣的话可以下载看看。

NSURLSession学习笔记的更多相关文章

  1. NSURLSession 学习笔记

    NSURLSession 学习笔记 一:NSURLSession 类似之前的NSURLConnection, 可配置每个session的 cookie,证书等网络连接配置信息 NSURLSession ...

  2. NSURLSession学习笔记(三)Download Task

    NSURLSession的Download Task用于完成下载任务,本文介绍如何创建断点续传的下载任务和后台下载任务. 我们直接从分析Demo入手: 故事板如下: 只有一个View Controll ...

  3. NSURLSession学习笔记(二)Session Task

    Session Task分为三种Data Task,Upload Task,Download Task.毫无疑问,Session Task是整个NSURLSession架构的核心目标. 下面写了一个简 ...

  4. NSURLSession学习笔记(一)简介

    一.URL Session的基本概念 1.三种工作模式: 默认会话模式(default):工作模式类似于原来的NSURLConnection,使用的是基于磁盘缓存的持久化策略,使用用户keychain ...

  5. iOS学习笔记13-网络(二)NSURLSession

    在2013年WWDC上苹果揭开了NSURLSession的面纱,将它作为NSURLConnection的继任者.现在使用最广泛的第三方网络框架:AFNetworking.SDWebImage等等都使用 ...

  6. iOS学习笔记22-推送通知

    一.推送通知 推送通知就是向用户推送一条信息来通知用户某件事件,可以在应用退到后台后,或者关闭后,能够通过推送一条消息通知用户某件事情,比如版本更新等等. 推送通知的常用应用场景: 一些任务管理APP ...

  7. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  8. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  9. PHP-会员登录与注册例子解析-学习笔记

    1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...

随机推荐

  1. javascript --- 只继承于原型

    正如上次所述,,出于效率考虑,我们应该尽可能的将一些可重用的属性和方法添加到原型中去. 如果养成了这个好习惯,我们仅仅依靠原型就能顺利的完成继承关系的构建了. 毕竟采用new her()方法将her的 ...

  2. Mvc项目架构分享之项目扩展

    Mvc项目架构分享之项目扩展 Contents 系列一[架构概览] 0.项目简介 1.项目解决方案分层方案 2.所用到的技术 3.项目引用关系 系列二[架构搭建初步] 4.项目架构各部分解析 5.项目 ...

  3. 在SharePoint中无代码开发InfoPath应用: 获取当前用户信息

    很多种不同的场景下,会需要得到当前的用户信息,例如需要根据当前用户判断组,进而控制权限. 首先InfoPath提供了一个userName方法,来实现这个目的,不过这个方法的问题是只能获得不包含域名的用 ...

  4. Android子线程更新UI主线程方法之Handler

    背景: 我们开发应用程序的时候,处于线程安全的原因子线程通常是不能直接更新主线程(UI线程)中的UI元素的,那么在Android开发中有几种方法解决这个问题,其中方法之一就是利用Handler处理的. ...

  5. 解析XML的几种方法之SAX解析

    假期总结不能停,坚持坚持....接下来总结一下XMl和json的解析和生成.. 解析XML的四种方法,即:DOM.SAX.JDOM和DOM4J 下面首先给出这四种方法的jar包下载地址: DOM:在现 ...

  6. 这些git技能够你用一年了

    用git有一年了,下面是我这一年来的git使用总结,覆盖了日常使用中绝大多数的场景.嗯,至少是够用一年了,整理出来分享给大家,不明白的地方可以回复交流. git设置关闭自动换行 git config ...

  7. Maven&&Ant使用

    “使用操作系统环境为CentOS-6.5” Ant使用 Maven使用 “Maven是一个项目管理和综合工具.Maven提供了开发人员构建一个完整的生命周期框架.开发团队可以自动完成项目的基础工具建设 ...

  8. JavaScript Patterns 2.9 Coding Conventions

    It’s important to establish and follow coding conventions—they make your code consistent, predictabl ...

  9. DLL分类

    使用def文件简化dll导出 VS查看DLL接口

  10. Windows Server 2008 R2安装WAMPSERVER无法启动的解决方法

    其实根本不算什么解决方法,会者不难的事.Windows Server 2008 R2(也包括其他版本的Windows)默认状态下安装WAMPSERVER经常是无法顺利启动WAMPSERVER的,尤其是 ...