网络-NSURLSession应用和原理
来源:互联网 发布:阴阳师挂机软件ios 编辑:程序博客网 时间:2024/06/05 03:28
网络-NSURLSession
1. 简介
NSRULConnection使用runloop来达到异步下载的,原理:Runloop保证重要的任务流畅执行; 分配固定时隙,实现单一线程异步;
connection 应用了runloop ,苹果不推荐使用底层设计理念,所以用封装更好NSURLSession;
NSURLSession 提供了配置会话缓存,协议,cookie和证书能力,这使得网络架构和应用程序可以独立各种,互不干扰; 还可以做会话任务,能加载数据,进行文件的上传和下载分别对应:DataTask,UploadTask,DownloadTask. 会话任务使默认挂起的,需要resume开始执行;
NSURLConnection完成的三个主要任务:获取数据(通常是JSON、XML等)、文件上传、文件下载。其实在NSURLSession时代,他们分别由三个任务来完成:NSURLSessionData、NSURLSessionUploadTask、NSURLSessionDownloadTask,这三个类都是NSURLSessionTask这个抽象类的子类
相对于NSURLConnection, NSURLSession支持任务的暂停,取消,恢复,并且默认任务运行在非主线程上
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更新,原理如图:
当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;
}
- 网络-NSURLSession应用和原理
- 网络篇 - 07.NSURLSession的应用场景
- 网络:NSURLSession
- NSURLSession的用法详解和设计原理
- iOS 网络访问框架 AFNetworking 和 NSURLSession
- IOS 开发进阶--多线程和网络--NSURLSession详细解
- 网络:NSURLSession 上传文件和代理的选择
- IOS学习 网络 NSURLSession和POST文件上传
- NSRULConnection网络应用原理
- 14-网络-NSURLSession
- 网络编程04---NSURLSession
- 网络开发---NSURLSession
- NSUrlSession 网络会话介绍
- iOS网络请求NSURLSession
- iOS NSURLSession 网络请求
- iOS 网络 - NSURLSession
- 网络请求(NSURLSession)
- 网络:NSURLSession 下载文件
- C#连接数据库代码(基础)
- [数位DP] HDU4734 F(x)
- 结构体指针之 段错误 详解(segmentation fault)
- Hbase存储数据结构
- 指针和数组的不同
- 网络-NSURLSession应用和原理
- 位运算:二进制中1的个数
- 字符串匹配
- Android面试题搜集
- 魔道研究
- AFNetworking框架使用
- 我终于深入参与了一个分布式系统了,好多想法不一样了
- [LeetCode]41 第一个缺失的整数
- 神经网络中的BP算法和Elman算法