最近需要用到下载,并显示下载进度,于是一阵苦搜,找到NSURLSession可以实现这个功能,心中不免大喜!当然,用AFNetworking也能实现,但是NSURLSession也是要了解的啊!
提到NSURLSession,最先要了解的就是NSURLSession的三个Task:NSURLSessionDataTask、NSURLSessionDownloadTask和NSURLSessionUploadTask。
- (void)viewDidLoad
{
[super viewDidLoad];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL: [NSURL URLWithString:@"Your URL"]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
//Your code
}];
[dataTask resume];
}
如果不是特别需要,使用sharedSession就可以获取NSURLSession的实例。在completionHandler中处理服务器返回的数据。默认情况下,建立的task会被挂起(NSURLSessionTaskStateXXXXX标识了任务状态),需要手动调用resume来启动任务。
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:[NSURL URLWithString:@"Your URL"]];
[downloadTask resume];
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
NSData *data = [NSData dataWithContentsOfURL:location];
//Your code
[session finishTasksAndInvalidate];
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
float progress = totalBytesWritten/ totalBytesExpectedToWrite;
dispatch_async(dispatch_get_main_queue(), ^{
self.progressView.progress = progress;
});
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
}
需要获取下载进度时,要实现NSURLSessionDownloadDelegate。在这里,我用defaultSessionconfiguration来创建一个session,并指定delegateQueue为nil,默认是串行队列(苹果提供了三个工厂来创建配置,除了我用到的默认配置外,还有ephemeralSessionConfiguration临时配置和backgroundSessionConfiguration后台配置)。创建完download task后,还是需要手动调用resume方法来启动下载。didFinishDownloadingToURL函数是下载完成后处理数据的,通过给出的URL能获得下载好的数据。在完成数据处理后,不使用session的话需要手动释放session,调用finishTasksAndInvalidate即可。didWriteData函数可以用来记录下载进度,在主线程中可以根据这个进度来更新Progress。didResumeAtOffset函数是在恢复下载时调用。
- (NSURLSession *)createBackgroundSession
{
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"Your Idntification"];
session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];
});
return session;
}
-(IBAction)cancel:(id)sender
{
if(!self.downloadTask)
return;
[self.downloadTask cancelByProducingResumeData:(^NSData *resumeData){
if(!resumeData)
return;
[self setResumeData:resumeData];
[self setDownloadTask:nil];
}];
}
如果要取消的下载的resumeData非空,我们还是把数据保存到视图控制器的resumeData属性中。
- (IBaction)resume:(id)sender
{
if(!self.resumeData) return;
self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];
[self.downloadTask resume];
[self setResumeData:nil];
}
我们先检查视图控制器中的resumeData是否有效,然后创建一个新的下载任务,传入resumeData。这个resumeData包含了所有session需要重新创建下载任务的信息,然后调用resume通知下载任务开始,并置视图控制器的resumeData为空。
在App退出后下载完成时通知操作系统
1 .在AppDelegate头文件中加入一个函数类型
#import
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (copy, nonatomic) void (^backgroundSessionCompletionHandler)();
@end
2 . 在AppDelegate的m文件中重写handleEventsForBackgroundURLSession函数,设置block
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler
{
[self setBackgroundSessionCompletionHandler:completionHandler];
}
3 . 在didFinishDownloadingToURL方法中调用我们自己写的下载完成后通知操作系统的方法
- (void)invokeBackgroundSessionCompletionHandler
{
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks){
NSUinteger count = [dataTasks count] + [uploadTasks count] + [downloadTasks count];
if(!count){
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
void(^backgroundSessionCompletionHandler)() = [delegate backgroundSessionCompletionHandler];
if(backgroundSessionCompletionHandler){
[delegate setBackgroundSessionCompletionHandler:nil];
backgroundSessionCompletionHandler();
}
}
}];
}