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

http://blog.csdn.net/majiakun1/article/details/38133789

NSURLSession学习笔记(三)Download Task的更多相关文章

  1. NSURLSession学习笔记

    NSURLSession学习笔记(一)简介 一.URL Session的基本概念 1.三种工作模式: 默认会话模式(default):工作模式类似于原来的NSURLConnection,使用的是基于磁 ...

  2. NSURLSession 学习笔记

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

  3. Oracle学习笔记三 SQL命令

    SQL简介 SQL 支持下列类别的命令: 1.数据定义语言(DDL) 2.数据操纵语言(DML) 3.事务控制语言(TCL) 4.数据控制语言(DCL)  

  4. [Firefly引擎][学习笔记三][已完结]所需模块封装

    原地址:http://www.9miao.com/question-15-54671.html 学习笔记一传送门学习笔记二传送门 学习笔记三导读:        笔记三主要就是各个模块的封装了,这里贴 ...

  5. JSP学习笔记(三):简单的Tomcat Web服务器

    注意:每次对Tomcat配置文件进行修改后,必须重启Tomcat 在E盘的DATA文件夹中创建TomcatDemo文件夹,并将Tomcat安装路径下的webapps/ROOT中的WEB-INF文件夹复 ...

  6. java之jvm学习笔记三(Class文件检验器)

    java之jvm学习笔记三(Class文件检验器) 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验,这就是今天的学习主题,cl ...

  7. VSTO学习笔记(三) 开发Office 2010 64位COM加载项

    原文:VSTO学习笔记(三) 开发Office 2010 64位COM加载项 一.加载项简介 Office提供了多种用于扩展Office应用程序功能的模式,常见的有: 1.Office 自动化程序(A ...

  8. Java IO学习笔记三

    Java IO学习笔记三 在整个IO包中,实际上就是分为字节流和字符流,但是除了这两个流之外,还存在了一组字节流-字符流的转换类. OutputStreamWriter:是Writer的子类,将输出的 ...

  9. NumPy学习笔记 三 股票价格

    NumPy学习笔记 三 股票价格 <NumPy学习笔记>系列将记录学习NumPy过程中的动手笔记,前期的参考书是<Python数据分析基础教程 NumPy学习指南>第二版.&l ...

  10. Learning ROS for Robotics Programming Second Edition学习笔记(三) 补充 hector_slam

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...

随机推荐

  1. IIS服务器管理学习

    工欲善其事必先利其器 首先给服务器配上强力的软件,用于安全防护和监控. 公司服务器用的阿里云的ECS,已经有防护和监控了,之后又选择额外加了一个安全狗 为了监控服务器上系统的各项运行指标,又买了听云平 ...

  2. OpenID 和 OAuth 的区别及第三方登录的安全隐患分析

    转自:http://itindex.net/detail/48552-openid-oauth-%E6%96%B9%E7%99%BB 发表时间:2014-03-13 19:09 | 作者:天梯梦 出处 ...

  3. [Vue]实例化Vue时的两种挂载方式el与$mount

    Vue 的$mount()为手动挂载,在项目中可用于延时挂载(例如在挂载之前要进行一些其他操作.判断等),之后要手动挂载上.new Vue时,el和$mount并没有本质上的不同. 1.el Vue实 ...

  4. Resource——资源的总结

    在xaml中,对于Style.DataTemplate.ControlTemplate.StoryBord等资源,可以放在UserControl.Resource.Windows.Resource.C ...

  5. Codeforces Round #424

    基本全是水题 第一题水,不过有hack点,先增后不变再减 #include<map> #include<set> #include<cmath> #include& ...

  6. 什么情况下用resultType和 resultMap

    MyBatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap 那什么情况下用resultType? resultMap 一般用在什么情况下?   如 ...

  7. 转:25个Java机器学习工具和库

    转自:http://www.cnblogs.com/data2value/p/5419864.html 本列表总结了25个Java机器学习工具&库: 1. Weka集成了数据挖掘工作的机器学习 ...

  8. QT 相关书籍

    qt qucik 核心编程 个人觉得此书写得非常之好....这位作者的另外一本虽然没看过,估计也不错 https://bbs.csdn.net/topics/390942701?list=lz qt5 ...

  9. qml 知识积累

    本文仅仅只是记录,怕日后遗忘. ListView相关应用 https://www.cnblogs.com/SaveDictator/p/8192391.html ListView 清空操作 https ...

  10. Jquery EasyUI Tree树形结构的Java实现(实体转换VO)

    前一阵做的OA项目,有一个是组织架构的树,因为是分开做的,我做的是Controller和页面,其他组做的Service和Dao,因为之前一直没有商量页面用什么框架做比较好,导致,Dao层取出来的数据都 ...