前言:
本文由DevDiv版主@jas 原创翻译,转载请注明出处!
原文:http://www.shinobicontrols.com/b ... day-1-nsurlsession/
大家都知道,过去的IOS系统网络处理是通过NSURLConnection来实现的。由于NSURLConnection通过全局状态来管理cookies和认证信息,这就意味着在某种情况下,可能同时存在两个不同的连接去使用这些公共资源。NSURLSession很好的解决了许多这种类似的问题。
本文连同附件一共讨论了三种不同的下载场景。本文会着重讲述有关NSURLSession的部分,整个项目就不再阐述了。代码可以在github回购。
NSURLSession状态同时对应着多个连接,不像之前使用共享的一个全局状态。会话是通过工厂方法来创建配置对象。
总共有三种会话:
1. 默认的,进程内会话
2. 短暂的(内存),进程内会话
3. 后台会话
如果是简单的下载,我们只需要使用默认模式即可:
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
配置对象有很多属性。例如,可以设置TLS安全等级,TLS决定你可以使用cookies和超时时间。还有两个非常有趣的属性:allowsCellularAccess和discretionary。前一个属性表示当只有一个3G网络时,网络是否允许访问。设置discretionary属性可以控制系统在一个合适的时机访问网络,比如有可用的WiFi,有充足的电量。这个属性主要针对后台回话的,所以在后台会话模式下默认是打开的。
当我们创建了一个会话配置对象后,就可以用它来创建会话对象了:
NSURLSession *inProcessSession; inProcessSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
NSString *url = @"http://appropriate/url/here"; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]]; NSURLSessionDownloadTask *cancellableTask = [inProcessSession downloadTaskWithRequest:request]; [cancellableTask resume];
我们需要实现一个代理方法来获取这个下载的内容:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location - { - // We've successfully finished the download. Let's save the file - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSArray *URLs = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]; - NSURL *documentsDirectory = URLs[0]; - NSURL *destinationPath = [documentsDirectory URLByAppendingPathComponent:[location lastPathComponent]]; - NSError *error; - // Make sure we overwrite anything that's already there - [fileManager removeItemAtURL:destinationPath error:NULL]; - BOOL success = [fileManager copyItemAtURL:location toURL:destinationPath error:&error]; if (success) - { - dispatch_async(dispatch_get_main_queue(), ^{ - UIImage *image = [UIImage imageWithContentsOfFile:[destinationPath path]]; self.imageView.image = image; - self.imageView.contentMode = UIViewContentModeScaleAspectFill; - self.imageView.hidden = NO; }); - } - else - { - NSLog(@"Couldn't copy the downloaded file"); - } - if(downloadTask == cancellableTask) - { - cancellableTask = nil; - } - }
上面的代理是下载成功的回调方法。下面代理方法也在NSURLSessionDownloadTaskDelegate代理中,不管任务是否成功,在完成后都会回调这个代理方法。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error - { - dispatch_async(dispatch_get_main_queue(), ^{ self.progressIndicator.hidden = YES; }); - }
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten BytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite - { - double currentProgress = totalBytesWritten / (double)totalBytesExpectedToWrite; dispatch_async(dispatch_get_main_queue(), ^{ - self.progressIndicator.hidden = NO; self.progressIndicator.progress = currentProgress; }); - }
- (IBAction)cancelCancellable:(id)sender - { - if(cancellableTask) - { - [cancellableTask cancel]; - cancellableTask = nil; - } - }
- (IBAction)cancelCancellable:(id)sender - { - if(self.resumableTask) - { - [self.resumableTask cancelByProducingResumeData:^(NSData *resumeData) - { - partialDownload = resumeData; self.resumableTask = nil; }]; - } - }
当新创建一个下载任务的时候,除了使用一个新的请求,我们也可以使用待恢复的下载数据:
if(!self.resumableTask) { if(partialDownload) { self.resumableTask = [inProcessSession downloadTaskWithResumeData:partialDownload]; } else { NSString *url = @"http://url/for/image"; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]]; self.resumableTask = [inProcessSession downloadTaskWithRequest:request]; } [self.resumableTask resume]; }
- (NSURLSession *)backgroundSession - { - static NSURLSession *backgroundSession = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.shinobicontrols.BackgroundDownload.BackgroundSession"]; - backgroundSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; }); - return backgroundSession; - }
NSString *url = @"http://url/for/picture"; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]]; self.backgroundTask = [self.backgroundSession downloadTaskWithRequest:request]; [self.backgrounTask resume];
-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler { self.backgroundURLSessionCompletionHandler = completionHandler; }
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location - { // Save the file off as before, and set it as an image view//... - if (session == self.backgroundSession) - { - self.backgroundTask = nil; - // Get hold of the app delegate - SCAppDelegate *appDelegate = (SCAppDelegate *)[[UIApplication sharedApplication] delegate]; - if(appDelegate.backgroundURLSessionCompletionHandler) - { // Need to copy the completion handlervoid (^handler)() = appDelegate.backgroundURLSessionCompletionHandler; appDelegate.backgroundURLSessionCompletionHandler = nil; - handler(); - } - } - }