断点续传
从上一篇文章中,我们了解了使用NSURLSession进行文件下载,我们在其基础上继续探索NSURLSession的断点续传。
在NSURLSession中,我们使用reumeData来存储下载的数据进度。
#import "ViewController.h"
#import "WTProgressBtn.h"
@interface ViewController ()<NSURLSessionDownloadDelegate>
/** 全局的网络会话(Session),管理所有的网络任务 **/
@property(nonatomic,strong)NSURLSession * session;
@property (weak, nonatomic) IBOutlet WTProgressBtn *progressView;
/** 全局的下载任务 **/
@property(nonatomic,strong)NSURLSessionDownloadTask * downloadTask;
/** 续传的数据 **/
@property(nonatomic,strong)NSData * reumeData;
@end
@implementation ViewController
-(NSURLSession *)session{
if (!_session) {
//config 提供了一个全局的网络环境配置,包括:身份验证,浏览器类型,cookie,缓存,超时...
NSURLSessionConfiguration * config =[NSURLSessionConfiguration defaultSessionConfiguration];
_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
}
return _session;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)start {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSURL * url = [NSURL URLWithString:@"http://127.0.0.1/abc.wmv"];
NSLog(@"开始");
self.downloadTask = [self.session downloadTaskWithURL:url];
[self.downloadTask resume];
NSLog(@"%@",[NSThread currentThread]);
});
}
//暂停
- (IBAction)pause:(id)sender {
NSLog(@"暂停");
[self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
//resumeData 续传的数据(下载了多少)
self.reumeData =resumeData;
//释放下载任务
// - 让我们task设置为week,就可以不用释放了
//因为任务都是由session发起的,而session对发起的任务都会持有一个强引用,但是不建议这样做
self.downloadTask = nil;
}];
}
//继续
- (IBAction)resume:(id)sender {
if (self.reumeData == nil) {
NSLog(@"无暂停任务");
return;
}
//所有的任务都是由session发起的
//使用续传数据启动下载任务
self.downloadTask = [self.session downloadTaskWithResumeData:self.reumeData];
//清空续传数据
self.reumeData = nil;
//所有的任务都是默认挂起的
[self.downloadTask resume];
}
#pragma mark - <NSURLSessionDownloadDelegate>
/*
iOS7.0,以下三个方法都是必须要实现的,到了iOS8.0只剩下下载完成是必须的
*/
/*1.下载完成方法*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
NSLog(@"完成 %@",location);
}
//2.下载进度
/**
1.session
2.downloadTask 调用代理方法的下载任务
3.bytesWritten 本次下载的字节数
4.bytesWritten 已经下载的字节数
5.totalBytesExpectedToWrite 期望下载的字节数 -> 文件总大小
*/
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
float progress = (float)totalBytesWritten / totalBytesExpectedToWrite ;
//回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
self.progressView.progress = progress;
});
NSLog(@"%f %@",progress,[NSThread currentThread]);
}
//3.下载续传数据
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{
}
@end
NSURLSession代理的队列
上面代码中的全局session中,我们创建session的时候使用了_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
队列:设置回调的代理方法在哪里执行
- 代理的队列 如果给nil 那么队列将在多个线程中执行
- 如果设置为[NSOperationQueue mainQueue] 那么将在主线程中执行
session会对代理进行强引用,如果任务执行结束后,不取消session,会出现内存泄露
因此我们对其进行处理
/*1.下载完成方法*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
NSLog(@"完成 %@",location);
//完成任务
[self.session finishTasksAndInvalidate];
//清空session
self.session = nil;
}
为什么session需要strong进行修饰呢?苹果为什么这么设计?
因为在真正的网络访问中
-在网络开发中,应该将所有的网络访问操作,封装到一个方法中,由一个统一的单例对象来负责所有的网络事件监听。
-session对代理(单例)进行强引用,单例本身就是一个静态的实例,本身就不需要释放
-AFN -> 需要建立一个AFN Manager
假如我们在下载未完成前,离开该页面,且停止下载任务,我们还需要
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
//取消会话
[self.session invalidateAndCancel];
self.session = nil;
}
在以后的文章中,我们还需要对AFN的封装实现原理进行更详细的分析,希望能让我们更好的提高对AFN第三方代码的理解。