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. 使用一般处理程序HTTPHandler下载文件

    一般来说我们可以用HTTPHandler来处理一些简单的逻辑,比如验证码.下载文件等. 以下载word文档为例讲解一下如何在HHTPHandler中下载文件,不限于word文档,如果下载其他文件,需要 ...

  2. SAP ST05数据跟踪使用

    有时我们想知道SAP操作,对数据库中的那些表进行的增删查改. 可以使用ST05跟踪SQL语句. ST05功能界面如下: 1---激活跟踪 2---结束跟踪 3---显示跟踪结果. 如果想跟踪SQL语句 ...

  3. Sharepoint学习笔记—习题系列--70-573习题解析 -(Q60-Q62)

    Question 60You have a SharePoint site collection that contains 100 subsites.You plan to create a Web ...

  4. [leetcode] Number of Islands

    Number of Islands Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. ...

  5. C安全编码--预处理

    建议和规则 建议: 用内联函数或静态函数代替与函数相似的宏 在宏参数名两边加上括号 宏替换列表应该加上括号 应该使用typedef定义编码类型 不要复用标准头文件名 理解连接标记或执行字符串化时的宏替 ...

  6. android 进程/线程管理(四)----消息机制的思考(自定义消息机制)

    关于android消息机制 已经写了3篇文章了,想要结束这个系列,总觉得少了点什么? 于是我就在想,android为什么要这个设计消息机制,使用消息机制是现在操作系统基本都会有的特点. 可是andro ...

  7. vi, vim 基本使用(1)

    本文介绍了vi (vim)的基本使用方法,但对于普通用户来说基本上够了!vi 编辑器是所有Unix及Linux系统下标准的编辑器,它的强大不逊色于任何最新的文本编辑器,这里只是简单地介绍一下它的用法和 ...

  8. PHP面试题集之基础题

    1.用PHP打印出前一天的时间格式是 2006-5-10 22:21:21 date_default_timezone_set('PRC'); //默认时区 echo "今天:", ...

  9. oracle向in语句传入参数查不出数据

    在oracle字符串中使用了in,但是查不出数据 string getModel = "select * from TB_YBSH where ID in :ids"; Oracl ...

  10. Vmware中Ubuntu的各种问题

    Question1: Ubuntu正在运行的时候手残强行关闭了Vmware软件,重启软件发现如下图所示的状态: ''' 完全黑屏怎么解决,提示信息是recovering journal就是说正在恢复日 ...