博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
NSURLSession学习笔记(三)Download Task
阅读量:6241 次
发布时间:2019-06-22

本文共 9594 字,大约阅读时间需要 31 分钟。

hot3.png

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

我们直接从分析Demo入手:

故事板如下:

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

头文件代码如下:

  1. #import <UIKit/UIKit.h>  

  2.   

  3.  ViewController : UIViewController <NSURLSessionDownloadDelegate>  

  4.   

  5. /* NSURLSessions */  

  6. @property (strongnonatomic)           NSURLSession *currentSession;    // 当前会话  

  7. @property (strongnonatomicreadonlyNSURLSession *backgroundSession; // 后台会话  

  8.   

  9. /* 下载任务 */  

  10. @property (strongnonatomicNSURLSessionDownloadTask *cancellableTask; // 可取消的下载任务  

  11. @property (strongnonatomicNSURLSessionDownloadTask *resumableTask;   // 可恢复的下载任务  

  12. @property (strongnonatomicNSURLSessionDownloadTask *backgroundTask;  // 后台的下载任务  

  13.   

  14. /* 用于可恢复的下载任务的数据 */  

  15. @property (strongnonatomicNSData *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. - (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为自己):

  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 (strongnonatomicNSData *partialData;  

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

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

  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.       

  11.     return backgroundSess;  

  12. }  

在创建后台下载任务时,应该使用后台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.       

  6.     [self setDownloadButtonsWithEnabled:NO];  

  7.     self.downloadedImageView.image = nil;  

  8.       

  9.     [self.backgroundTask resume];  

  10. }  

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

  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中的方法,该方法只有下载成功才被调用:

  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.       

  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即可:

  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. /* 根据下载进度更新视图 */  

  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指定了恢复下载时的文件位移字节数:

  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基于改写,内容基本一致。

原来的Demo也有一篇博客对应:。

本文的也已经上传,有兴趣的话可以下载看看。

转载于:https://my.oschina.net/u/2491538/blog/541444

你可能感兴趣的文章
Git 移除某些文件
查看>>
poj2940
查看>>
django做form表单的数据验证
查看>>
【OpenFOAM】——OpenFOAM入门算例学习
查看>>
STL UVA 11991 Easy Problem from Rujia Liu?
查看>>
模拟 URAL 1149 Sinus Dances
查看>>
Oracle 11G 数据库迁移【expdp/impdp】
查看>>
17.EXTJs 中icon 与iconCls的区别及用法!
查看>>
3.mybatis实战教程(mybatis in action)之三:实现数据的增删改查
查看>>
Caused by: Unable to load bean: type: class:com.opensymphony.xwork2.ObjectFactory - bean - jar
查看>>
让你拥有超能力:程序员应该掌握的统计学公式
查看>>
互联网组织的未来:剖析 GitHub 员工的任性之源
查看>>
Java 开源博客 Solo 1.4.0 发布 - 简化
查看>>
Oracle巡检
查看>>
【转载】胜者树
查看>>
查看mysql数据库存放的路径|Linux下查看MySQL的安装路径
查看>>
selenium+testNG+Ant
查看>>
1024程序员节,你屯书了吗?(内含福利)
查看>>
移动端JS 触摸事件基础
查看>>
Flex拖动原来如此简单
查看>>