网络-NSURLSession应用和原理
2016-03-05 16:32
495 查看
网络-NSURLSession
1. 简介NSRULConnection使用runloop来达到异步下载的,原理:Runloop保证重要的任务流畅执行; 分配固定时隙,实现单一线程异步;
connection 应用了runloop ,苹果不推荐使用底层设计理念,所以用封装更好NSURLSession;
NSURLSession 提供了配置会话缓存,协议,cookie和证书能力,这使得网络架构和应用程序可以独立各种,互不干扰; 还可以做会话任务,能加载数据,进行文件的上传和下载分别对应:DataTask,UploadTask,DownloadTask. 会话任务使默认挂起的,需要resume开始执行;
![](http://images.cnitblog.com/blog/62046/201410/220832288874218.png)
NSURLConnection完成的三个主要任务:获取数据(通常是JSON、XML等)、文件上传、文件下载。其实在NSURLSession时代,他们分别由三个任务来完成:NSURLSessionData、NSURLSessionUploadTask、NSURLSessionDownloadTask,这三个类都是NSURLSessionTask这个抽象类的子类
相对于NSURLConnection, NSURLSession支持任务的暂停,取消,恢复,并且默认任务运行在非主线程上
![](http://images.cnitblog.com/blog/62046/201410/220832302934690.png)
Session会话 分类
使用share单例获取的全局会话 是系统内部的,所用应用程序都能使用,所以此session不能设置代理监听; 扩展:其实此会话任务不仅不在一个线程,甚至不再一个进程;
使用NSURLSessionConfigraton管理生成session;
defaultSessionConfiguration :进程内会话,用硬盘来缓存数据.账户信息存储到钥匙链,如果有cookie会携带cookie
ephemeralSessionConfiguration: 临时的进程内会话(数据存于内存),不会将cookie,当程序退出数据就会消失;
backgroundSessionConfiguration: 后台会话,相比默认会话,任务是交给后台守护线程完成的,属于别的进程非程序本身,来进行网络数据处理;(所以程序崩溃也不会中断下载,但是如果用户使用多界面强制退关闭程序,Session会断开连接.)
NSURLSessionConfiguration的属性与功能
可以统一添加设置请求头信息
config.HTTPAdditionalHeaders = @{"Authorization":xxxx}
设置主机的最大连接数 :
Config.HTTPMaximumConnectionsPerHost = 5
系统自动选择最佳网络下载:
Config.discretionary=YES;
设置请求超时和缓存策略
requestCachePolicy/Config.timeoutIntervalForRequest=15;
是否允许蜂窝网络下载
Config.allowsCellularAccess=true
等等
数据请求
使用Data任务//1.创建请求 NSURLrequest *request = [NSURLRequest requestWithURL:url]; //2.创建会话 (此处全局会话) NSURLSession *session=[NSURLSession sharedSession]; //2.1 从会话创建Data任务 后面需要开启任务 NSURLSessionDataTask *dataTask=[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (!error) { id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]; NSLog(@"%@",result); }else{ NSLog(@"error is :%@",error.localizedDescription); } }]; [dataTask resume]; //启动任务 }
//可以精简为 [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]; }] resume];
文件下载
使用NSURLSessionDownloadTask 下载文件的过程与前面差不多,需要注意的是文件下载文件之后会自动保存到一个临时目录(temp),需要开发人员自己将此文件重新放到其他指定的目录中。 或者直接在内存里面显示;, 默认异步的;1. 内存暴涨问题解决
NSURLSession 自动不会出现内存暴涨情况,
//全局会话 创建 downLoadTask任务; //参数location: 下载完成后文件路径 (需要我们重新指定) [[[NSURLSession sharedSession] downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { //重新指定路径 NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; path = [path stringByAppendingPathComponent: fileName]; //复制文件过去 [[NSFileManager defaultManager] copyItemAtPath:location.path toPath:path error:NULL]; }] resume]; //启动任务
下载图片并显示
downLoad 不会占硬盘缓存 , Data会生成缓存;所以下载显示用download ;
NSURL *url = [NSURL URLWithString:@“http:/127.0.0.1/uploads/xx.png"]; [[self.session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ NSData *data = [NSData dataWithContentsOfURL:url]; self.imageView.image = [UIImage imageWithData:data]; }); }] resume];
//可以设置在缓存文件夹,并且设置MemoryCapactity 空间大小 和 diskCapacity 缓存空间大小 NSURLCache *cache = [NSURLCache alloc] initWithMemoryCapacity: 1024*1024 diskCapacity:1024*1024*5 diskPath:@"images"]; [ NSURLCache setSharedURLCache : cache];
2. 下载进度问题
跟踪下载进度,跟之前一样,使用NSURLSessionDownloadDelegate代理方法( 其中关系:NSURLSessionDownloadDelegate —> NSURLSessionTaskDelegate –> NSURLSessionDelegate
);因为不能使用全局Session设置代理,所以要监听进度的话,上面代码的Session需要更换;可以使用NSURLSessionConfiguration来创建;且注意不能使用带block回调的方法,代理方法优先级比block回调低,会被block覆盖
//懒加载Session 并设置代理 - (NSURLSession *)session{ if (_session == nil) { NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];// 创建session 指定的队列 ,决定了代理和block的执行队列. nil表示默认新队列; } return _session; } / //使用非全局session [[self.session downloadTaskWithURL:url] resume];
代理方法如下:
//下载完成 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSLog(@"%@",[NSThread currentThread]); NSLog(@"下载完成 : %@",location); } //续传的方法 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes { NSLog(@"续传"); } //获取进度的方法 (多次调用获取进度) - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { float process = totalBytesWritten * 1.0 /totalBytesExpectedToWrite; NSLog(@"下载进度: %f",process); }
注意:
下载任务使异步执行的,即是把下载任务添加到主队列(Session是高度异步的)
所有代理方法都是主线程上异步执行(当指定为主队列时);
3. 断点续传 -下载的暂停取消继续.
记录下下载任务 downLoadtask.
取消: cancel .不能续传
暂停/挂起: cancelByProducinResumeData
self.downloadTask cancelByProducinResumeData:^(NSData *resumeData) { self.resumeData = resumeData; //记录续传数据 self.resumeData writeToFile:self.path atomically:YES]; //暂停 要存储当前数据到文件;// 只是一个配置信息;方便续传时获得range 值; } //进行保存沙盒操作等 self.downloadTask = nil; //防止点击多次,暂停多次清空resumeData数据
恢复
//续传,先加载暂停的数据 if(self.downloadTask !=nil ){//判断如果没点暂停就不执行继续下载. return ; } NSFileManager *fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath:self.path]) { //如果文件存在 self.resumeData = [NSData dataWithContentsOfFile:self.path]; } if (self.resumeData == nil) { return; } //继续下载 self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData ]; [self.downloadTask resume]; self.resumeData = nil; //防止点击多次, 创建多个续传
扩展: reumeData其实是plist文件,只是几率下载的信息,如url,path等,而不是存储的下载数据;它之所以可以续传,是因为存储了断点续传核心range头.
2. loadTask 下载完会删除,但是和connection 的 DownloaderDelegate 的产权保护删除不同,后者是整个过程都不能获取下载数据;
3. 如果突然停电,reumeData的range数据不能保存,但是可以从已经下载到的文件获取到filesize,再解析plist文件,给reumeData重设range;(少见)
4. 文件下载完成解压缩 程序自动解压,使用SSZipArchive框架.(注意:用词框架需要导入系统一个库 liba.tbd . 程序设置最后一行)
扩展: 后台下载
使用后台会话进行下程序退出到后台也能正常下载完成,但是程序在后台UI无法更新,不能获取进度;这时,我们需要通过应用程序代理进行UI更新,原理如图:
![](http://images.cnitblog.com/blog/62046/201410/220832346215200.png)
当NSURLSession在后台开启几个任务之后,如果其中有任务完成,系统就会会调用此APP的代理方法:
-(void)application: (UIApplication * ) application handleEventsForBackgroundURLSession:identifier completionHandler:(^());completionHandlr里进行完成的操作. 通常我们会保持此对象,直到最后一个任务完成;
此时会重新通过会话标识(config中设置的)找到对应会话并调用NSURLSession的-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession * )session代理方法例进行UI的更新.并调用completionHandler通知系统已经完成所有操作。具体如下:
-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler{ //backgroundSessionCompletionHandler是自定义的一个属性 self.backgroundSessionCompletionHandler=completionHandler; } -(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{ AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; //Other Operation.... if (appDelegate.backgroundSessionCompletionHandler) { void (^completionHandler)() = appDelegate.backgroundSessionCompletionHandler; appDelegate.backgroundSessionCompletionHandler = nil; completionHandler(); } }
Session的文件上传
put直接以文件的方式写入 ;post需要服务器端脚本支持 ;
1. 通过session发送put请求上传文件.
如果直接上传返回状态码401 –没有授权;PUT需要授权身份验证;请求头有一项Authorization: Basic YWRtaW46MTIzNDU2 ;base64编码的账号和密码;
PUT方式,上传,如果服务器没有此数据,那么会创建,如果有同名,会更新;
如果用post,要设置Content-Type、Range、User-Agent、Authorization
NSURL *url = [NSURL URLWithString:@"http:/192.168.31.244/uploads/123.png"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.HTTPMethod = @"put"; //添加请求头的授权信息 Authorization: Basic YWRtaW46MTIzNDU2 [request setValue:[self getAuthorizationStr] forHTTPHeaderField:@"Authorization"]; //getAut方法自定义编码账号,格式:admin:123456 //获取文件路径 NSString *path = [[NSBundle mainBundle] pathForResource:@"xx" ofType:@"png"]; //创建上传任务 [[self.session uploadTaskWithRequest:request fromFile:[[NSURL alloc] initFileURLWithPath:path] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"%@ %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding],response); }] resume]; }
上传进度条
使用代理方法获取.NSURLSessionTaskDelegate;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{ //bytesSent 本次上传的字节数 //totalBytesSent 总共上传的字节数 //totalBytesExpectedToSend 文件的总大小 float process = (float)totalBytesSent / totalBytesExpectedToSend; }
Delete方式删除文件
- (void)deleteFile{ NSURL *url = [NSURL URLWithString:@"http:/127.0.0.1/uploads/123.png"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.HTTPMethod = @"delete"; //添加请求头的授权信息 Authorization: Basic YWRtaW46MTIzNDU2 [request setValue:[self getAuthorizationStr] forHTTPHeaderField:@"Authorization"]; [[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"%@ %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding],response); }] resume]; }
NSURLSession的使用注意
一般session代理设控制器,那么self.session和控制器间会循环引用;解决方法;
网络操作完成之后,取消操作:[session finishTaskAndInvalidate]; 并 session =nil;
因为session invalidate之后无法再次使用,所以设为nil之后,下次使用时会懒加载创建;
但是这样实现,每一次操作完成都会创建销毁session,麻烦,所以我们要把解决代码下载ViewWillDisapper方法里,这样只有在控制器释放时候才销毁,这样不会重复创建销毁session;
完整代码如下:
- (void)viewWillDisappear:(BOOL)animated{ [super viewWillDisappear:animated]; //一旦销毁,session和代理之间的引用,session和block之间的关系就被干掉 //所以无法再次使用 [self.session invalidateAndCancel]; self.session = nil; }
相关文章推荐
- NSRULConnection网络应用
- NSRULConnection网络应用原理
- http和socket之长连接和短连接
- Universal-Image-Loader,android-Volley,Picasso、Fresco和Glide五大Android开源组件加载网络图片的优缺点比较
- nginx与ios实现https双向认证
- 登山-C#-关于TcpListener的AcceptTcpClient()方法造成线程阻塞,进而程序无法彻底关闭的问题
- Web缓存基础:术语、HTTP报头和缓存策略
- tcp数据发送过快的处理
- 【翻译+原创】Deep Learning Face Representation from Predicting 10,000 Classes 论文笔记
- Linux TCP/IP 协议栈调优
- Linux TCP/IP 协议栈调优
- TCP/IP 相关知识点与面试题集(转)
- 简单的 winsocket TCP通信
- Linux(Centos)中tcpdump参数用法详解(转)
- OkHttp的使用
- TCP面向连接中的“连接”究竟是什么,可靠与不可靠(转)
- TCp/IP实验一
- 自写网络流24题总结
- AFNetworking使用
- TCP/IP四层模型