NSURLSession是iOS7中新的网络接口。
NSURLSession提供的功能:
NSURLSession工作流程:
1.创建一个NSURLSessionConfiguration,用于第二步创建NSSession时设置工作模式和网络配置。
工作模式:
1.一般模式(default):这实际上与NSURLConnection的网络协议栈是一样,具有相同的共享NSHTTPCookieStorage,共享NSURLCache和共享NSURLCredentialStorage。
2.及时模式(ephemeral):没有持久性存储的缓存,Cookie或证书。这对于实现像秘密浏览功能的功能来说,是很理想的。
3.后台模式(background):独特之处在于,它会创建一个后台会话。后台回话不同于常规的,普通的会话,它甚至可以在应用程序挂起,退出,崩溃的情况下运行上传和下载任务。初始化时指定的标识符,被用于向任何可能在进程外恢复后台传输的守护进程提供上下文。方法中identifier参数指定了会话的ID,用于标记后台的session。
NSURLSessionConfiguration有两个属性:
1.allowsCellularAccess属性指定是否允许使用蜂窝连接。
2.dscretionary属性为YES时表示当程序在后台运作时由系统自己选择最佳的网络连接配置,该属性可以节省通过蜂窝连接的带宽。
在使用后台传输数据的时候,建议使用discretionary属性,而不是allowsCellularAccess属性,因为它会把WiFi和电源可用性考虑在内。补充:这个标志允许系统为分配任务进行性能优化。这意味着只有当设备有足够电量时,设备才通过Wifi进行数据传输。如果电量低,或者只仅有一个蜂窝连接,传输任务是不会运行的。后台传输总是在discretionary模式下运行。
2.创建一个NSURLSession,系统提供了两个创建方法:
sessionWithConfiguration:
sessionWithConfiguration:delegate:delegateQueue:
3.创建一个NSURLRequest
4.调用NSURLSession对象提供的Task函数,创建一个NSURLSessionTask。
根据职能不同Task有三种子类:
1.NSURLSessionUploadTask:上传用的Task,传完以后不会再下载返回结果。
2.NSURLSessionDownloadTask:下载用的Task。
3.NSURLSessionDataTask:可以上传内容,上传完后返回再进行下载。(NSURLSessionDataTask 和 NSURLSessionDownloadTask 继承自NSURLSession,NSURLSessionUploadTask 继承自NSURLSessionDataTask)
5.当不再需要连接调用Session的invalidateAndCancel直接关闭,或者调用finishTasksAndInvalidate等待当前Task结束后关闭。这时Delegate会收到URLSession:didBecomeInvalidWithError:这个事件。Delegate收到这个事件之后会被解引用。
6.如果是一个BackgroundSession,在Task执行的时候,用户切到后台,Session会和ApplicationDelegate做交互。当程序切换到后台后,在BackgroundSession中的Task还会继续下载。
��1: 数据任务NSURLSessionDataTask:
// 创建请求路径
NSString *urlStr = @"http://www.baidu.com/thcgi/get_todo_list?uid=3&oid=1";
NSURL *url = [NSURL URLWithString:urlStr];
// 创建请求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 创建数据任务
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
// 不是在主线程执行
NSLog(@"%@", dict);
}];
[task resume];
��2:上传任务NSURLSessionUploadTask:
// 创建请求路径
NSString *urlStr = @"http://www.baidu.com/thcgi/get_todo_list";
NSURL *url = [NSURL URLWithString:urlStr];
// 创建请求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 创建参数字符串
NSString *parmStr = @"uid=3&oid=1";
// 将参数字符串转成NSData类型
NSData *parmData = [parmStr dataUsingEncoding:NSUTF8StringEncoding];
// 创建上传任务
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:parmData completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 不是在主线程执行
}];
[uploadTask resume];
��3:下载任务NSURLSessionDownloadTask:
// 下载任务也需要一个请求,但不同之处在于它们的completionHandler。数据和上传任务在完成时立即返回,但下载任务将数据写入本地的临时文件。completionHandler有责任将文件从它的临时位置移动到一个永久位置。
// 创建请求路径
NSString *urlStr = @"http://p1.pichost.me/i/40/1639665.png";
NSURL *url = [NSURL URLWithString:urlStr];
// 创建请求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 下载任务
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 不是在主线程
// location: 是一个临时位置,如果需要将下载的文件永久保存,需要将文件从它的location临时位置移动到一个永久位置。
NSData *data = [NSData dataWithContentsOfURL:location];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithData:data];
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(10, 100, 100, 100)];
iv.image = image;
iv.backgroundColor = [UIColor redColor];
[self.view addSubview:iv];
});
}];
[downloadTask resume];
��4:断点续传
- (void)start:(UIButton *)btn
{
NSLog(@"Start download task");
// 用NSURLSession和NSURLRequest创建网络任务
_task = [[self session] downloadTaskWithRequest:[self request]];
[_task resume];
}
- (void)pause:(UIButton *)btn
{
NSLog(@"Pause download task");
if (_task) {
// 取消下载任务,把已下载数据存起来
[_task cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
_partialData = resumeData;
_task = nil;
}];
}
}
- (void)stop:(UIButton *)btn
{
NSLog(@"Stop download task");
if (!_task) {
// 判断是否有已下载数据,有的话就断点续传,没有就完全重新下载
if (_partialData) {
_task = [[self session] downloadTaskWithResumeData:_partialData];
} else {
_task = [[self session] downloadTaskWithRequest:[self request]];
}
}
[_task resume];
}
// 创建session
- (NSURLSession *)session
{
// 创建NSURLSession
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
// delegateQueue: 决定NSURLSessionDownloadDelegate代理的方法是在哪个线程中执行
// 如果delegateQueue设置为[NSOperationQueue mainQueue],则NSURLSessionDownloadDelegate代理的方法是在主线程中执行
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]];
return session;
}
// 创建请求
- (NSURLRequest *)request
{
// 创建请求
NSURL *url = [NSURL URLWithString:@"http://p1.pichost.me/i/40/1639665.png"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
return request;
}
// 创建文件本地保存目录,将文件保存在documents文件夹下
- (NSURL *)createDirectoryForDownloadItemFromURL:(NSURL *)location
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *urls = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *documentsDirectory = urls[0];
return [documentsDirectory URLByAppendingPathComponent:[location lastPathComponent]];
}
// 把文件拷贝到指定目录
- (BOOL)copyTempFileAtURL:(NSURL *)location toDestination:(NSURL *)destination
{
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:destination error:NULL];
[fileManager copyItemAtURL:location toURL:destination error:&error];
if (error == nil) {
return true;
} else {
NSLog(@"%@", error);
return false;
}
}
#pragma mark - NSURLSessionDownloadDelegate
// 下载完成后调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
// 下载成功后,文件是保存在一个临时目录的,需要开发者自己拷到放置该文件的目录
NSLog(@"Download success for URL: %@", location.description);
NSURL *destination = [self createDirectoryForDownloadItemFromURL:location];
BOOL success = [self copyTempFileAtURL:location toDestination:destination];
if (success) {
// 文件保存成功后,使用GCD调用主线程把图片文件显示在UIImageView中
UIImage *image = [UIImage imageWithContentsOfFile:[destination path]];
_imageView.image = image;
_imageView.contentMode = UIViewContentModeScaleAspectFit;
_imageView.hidden = NO;
} else {
NSLog(@"Meet error when copy file");
}
_task = nil;
}
// 每次接收到服务器数据时调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
// bytesWritten:每次下载数据的容量大小
// totalBytesWritten:已经下载数据的容量大小
// totalBytesExpectedToWrite:正在下载的文件总容量大小
// 刷新进度条的delegate方法,同样地,获取数据,调用主线程刷新UI
double currentProgress = totalBytesWritten/(double)totalBytesExpectedToWrite;
_progressView.progress = currentProgress;
_progressView.hidden = NO;
}
// 恢复下载时调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
// fileOffset:已经下载数据的容量大小
// expectedTotalBytes:正在下载的文件总容量大小
}