iOS7 中的新加入的下载类NSURLSession(随ios版本更新而更新)
想详细的了解网络下载的相关知识,要仔细阅读URL Loading System Programming Guide
这里有篇好文章(http://www.shinobicontrols.com/blog/posts/2013/09/20/ios7-day-by-day-day-1-nsurlsession/)
这里是它的译文 http://blog.csdn.net/ios_suda/article/details/12745457,感谢作者!
这里是另一篇好文章 http://www.cnblogs.com/biosli/p/iOS_Network_URL_Session.html
以往的下载用的是NSURLConnection类,现在这个NSURLSession有什么好处?
In the past networking for iOS was performed using NSURLConnection
which used the global state to manage cookies and authentication. Therefore it was possible to have 2 different connections competing with each other for shared settings.NSURLSession
sets out to solve this problem and a host of others as well.
上面说的是NSURLConnection用的是共享的cookies和认证,可能引起冲突,而NSURLSession解决了这个问题(?具体例子?)。
现在的这个NSURLSession提供了取消下载,恢复下载(断点续传),提供下载进度,后台下载,而且代理方法条理清晰,简洁明了。
涉及到的类分为3大种类(共计10个class)
NSURLSession(设置的代理可以实现的协议有 NSURLSessionDelegate ,NSURLSessionDownloadDelegate,NSURLSessionTaskDelegate,NSURLSessionDataDelegate),
NSURLSessionConfiguration,
NSURLSessionTask(子类有NSURLSessionUploadTask, NSURLSessionDownloadTask, NSURLSessionDataTask)。
使用NSURLSession 进行下载主要步奏如下:
1. 创建NSURLSessionConfiguration
2.利用NSURLSessionConfiguration创建NSURLSession
3.利用NSURLSession创建具体的NSURLSessionTask
4.调用NSURLSessionTask的resume启动下载
在创建SessionTasks时,系统提供了多重方法,大体分为带completion block 和 不带 2类,对于带completion block的初始化方法,有如下解释:
The task bypasses calls to delegate methods for response and data delivery, and instead provides any resulting NSData, URLResponse, and NSError objects inside the completion handler. Delegate methods for handling authentication challenges, however, are still called.
这段文字有点绕口,我翻译一下:创建的task会绕过为了传送数据和response而对delegate的发起的调用,作为替代,它会以在completion handler中提供作为结果的 data,response和 error。但是,仍然会调用 delegate中为了处理认证的方法。
简单地说,就是一但使用了completion block这种初始化方法,即使又指定了deleagate,与之对应的delegate中的类似方法也不会再调用了。
这里有一个非常重要的功能,后台下载功能。在以前,程序处于挂起状态后,即使申请了后台运行,也仅仅有短暂运行时间,这段运行时间往往不能够保证下载大型的数据。另外如果程序被强制结束了(系统结束的,不是被用户结束的),那么下载更不可能继续进行。NSURLSession的后台下载功能解决了上面的问题,我们看一下使用NSURLSession如何进行后台下载。
使用后台的主要特别之处是要利用
class func backgroundSessionConfigurationWithIdentifier(_ identifier: String) -> NSURLSessionConfiguration
Use this method to initialize a configuration object suitable for transferring data files while the app runs in the background. A session configured with this object hands control of the transfers over to the system, which handles the transfers in a separate process. In iOS, this configuration makes it possible for transfers to continue even when the app itself is suspended or terminated.
If an iOS app is terminated by the system and relaunched, the app can use the same identifier
to create a new configuration object and session and retrieve the status of transfers that were in progress at the time of termination. This behavior applies only for normal termination of the app by the system. If the user terminates the app from the multitasking screen, the system cancels all of the session’s background transfers. In addition, the system does not automatically relaunch apps that were force quit by the user. The user must explicitly relaunch the app before transfers can begin again.
方法创建一个在可以在后台运行的NSURLSessionConfiguration,利用这个设置启动的NSURLSessionTask,都可以在后台运行!
读到这里,我有2个问题:
关于挂起时的下载,我想问的问题是,能一直下载到全部下载完?如果过了几个小时还没下完怎么办?下载的速度和前台运行时的速度一样?
关于被系统结束时的下载,我想问的问题是,怎么能模拟一下,测试效果?
先看2个回掉方法说明:
optional func URLSessionDidFinishEventsForBackgroundURLSession(_ session: NSURLSession) In iOS, when a background transfer completes or requires credentials, if your app is no longer running, your app is automatically relaunched in the background, and the app’s UIApplicationDelegate is sent an application:handleEventsForBackgroundURLSession:completionHandler: message. This call contains the identifier of the session that caused your app to be launched. Your app should then store that completion handler before creating a background configuration object with the same identifier, and creating a session with that configuration. The newly created session is automatically reassociated with ongoing background activity. When your app later receives a URLSessionDidFinishEventsForBackgroundURLSession: message, this indicates that all messages previously enqueued for this session have been delivered, and that it is now safe to invoke the previously stored completion handler or to begin any internal updates that may result in invoking the completion handler.
optional func application(_ application: UIApplication,
handleEventsForBackgroundURLSession identifier: String,
completionHandler completionHandler: () -> Void) completionHandler
The completion handler to call when you finish processing the events. Calling this completion handler lets the system know that your app’s user interface is updated and a new snapshot can be taken. whether they finished successfully or resulted in an error. The app also calls this method if authentication is required for one or more transfers. Use this method to reconnect any URL sessions and to update your app’s user interface. For example, you might use this method to update progress indicators or to incorporate new content into your views. After processing the events, execute the block in the completionHandler parameter so that the app can take a new snapshot of your user interface. If a URL session finishes its work when your app is not running, the system launches your app in the background so that it can process the event. In that situation, use the provided identifier to create a new NSURLSessionConfiguration and NSURLSession object. You must configure the other options of your NSURLSessionConfiguration object in the same way that you did when you started the uploads or downloads. Upon creating and configuring the new NSURLSession object, that object calls the appropriate delegate methods to process the events. If your app already has a session object with the specified identifier and is running or suspended, you do not need to create a new session object using this method. Suspended apps are moved into the background. As soon as the app is running again, the NSURLSession object with the identifier receives the events and processes them normally. At launch time, the app does not call this method if there are uploads or downloads in progress but not yet finished. If you want to display the current progress of those transfers in your app’s user interface, you must recreate the session object yourself. In that situation, cache the identifier value persistently and use it to recreate your session object.
//
关于第一个问题:
我首先测试了一个40m的下载文件,ios8 iphone5c,下载了2分多后,调用了
application:handleEventsForBackgroundURLSession:completionHandler 和
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session NS_AVAILABLE_IOS(7_0);
方法,正常结束。
第二次测试一个90多m的下载文件,过了25分,没有结束,重新唤醒程序,看到只下载了40多m,不知道是时间过长停止下载了,还是还在慢慢地下载。。。
第三次继续测试90m的文件,6分钟后唤醒程序,下载了百分之42,期间有很长一段时间在待机状态,感觉这可以证明在待机状态也是可以继续下载的,但是不清楚有没有时间限制。唤醒后,又回到后台状态,等待结束。8分钟后,下载成功结束!手机在待机状态也执行了回掉方法。期间也有很长的一段待机时间。这次测试时,没有运行其他的程序,不知道其他程序的运行会不会影响后台下载。
第四期,重复第二次测试,这次多等待一会。。。。25分,不够耐心,手动唤醒,百分之94,我决定再实验一次。
第五次,重复第二次测试,45分,期间正常使用电话,不知道是不是按错了,点回来session没有继续,不知道为什么。
第六次,在第五次基础上再用同一个session发起另一个请求,20分钟后查看,第五次实验的task有了反应。。。0.85进度,而第六次的进度是0.45. 我感觉这2个task都处于停止状态!
由上面的实验可以测出,过长时间的后台下载是不实际的!仔细想想ios提供的后台机制就该想到,这种无限制的后台运行不可能存在。。。。不然不和android一样费电了吗。。。
关于第二个问题,我用用模拟器模拟一下内存溢出,程序下载继续执行,貌似不能说明什么问题。。。
AFNetworking框架也为NSURLSession做了相应的调整。
关于AFNetworking framework的基本使用方法,请参见github上的说明文档,也可以参见readme文档。
最新版的AFNetworking framework 提供了2套网络上传下载的解决方案,一套基于以前的NSURLConnection,另一套基于新出的NSURLSession。由于NSURLConnection产生的较早,没有集成与线程相关的代码,所以如果在其他线程中进行下载,需要自己建立NSOperation Queue,并把相应的operation加入到quque中。注意,如果你要支持ios7以前的版本,那么必须用这套方案。主要涉及AFURLConnectionOperation,AFHTTPRequestOperation,AFHTTPRequestOperationManager3个类,其中最重要的就是AFURLConnectionOperation类,它提供了多个block属性,可以让你指定block,实现认证,显示进度等功能。
示例代码:
NSMutableArray *mutableOperations = [NSMutableArray array];
for (NSURL *fileURL in filesToUpload) {
NSURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:fileURL name:@"images[]" error:nil];
}]; AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; [mutableOperations addObject:operation];
} NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:@[...] progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
NSLog(@"%lu of %lu complete", numberOfFinishedOperations, totalNumberOfOperations);
} completionBlock:^(NSArray *operations) {
NSLog(@"All operations in batch complete");
}];
[[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];
-----------------------------------------------------------------------------------------------------------
而NSURLSession不同,它本身就拥有了task的概念,使用起来也比较方便,例如
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL]; NSURL *filePath = [NSURL fileURLWithPath:@"file://path/to/image.png"];
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request fromFile:filePath progress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"Success: %@ %@", response, responseObject);
}
}];
[uploadTask resume];
这里task的代理没有单独的设置,都是调用session对象的代理。而且session对象的代理在初始化时就要指定。在创建task时,如果你使用了带complete回调块的创建函数,系统就不会再调用session中相应的进度监听回掉方法。如果想调用代理中的进度方法,就不能使用带complete回调块的创建函数。
task 创建后不会自动开始,需要调用resume函数开始。
afnetworking 和 nsurlsession 中都没有提供串行下载的方法,即无法在一个下载完成后再继续另一个下载。需要研究下是不是真的不行????????
------------------------------------------------------------------------
再说一点题外话:
AFNetworking 的另一大功能是它提供了一套序列化的代码,看一下demo:
NSURL *URL = [NSURL URLWithString:@"http://example.com/resources/123.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
op.responseSerializer = [AFJSONResponseSerializer serializer];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error: %@", error);
}];
[[NSOperationQueue mainQueue] addOperation:op]; -----------------------------------------------------------------
iOS7 中的新加入的下载类NSURLSession(随ios版本更新而更新)的更多相关文章
- iOS7 新后台及下载SDK介绍
在iOS7以前的系统中,App默认是不能后台运行的,如果要后台运行,可以采用以下两类方法: (1)使用beginBackgroundTaskWithExpirationHandler函数,向系统申请一 ...
- iOS7开发中的新特性
iOS7到现在已经发布了有一段时间了.相信你现在已经了解了它那些开创性的视觉设计,已经了解了它的新的API,比如说SpirteKit,UIKit Dynamics以及TextKit,作为开发者 ...
- Java日期时间API系列13-----Jdk8中java.time包中的新的日期时间API类,时间类转换,Date转LocalDateTime,LocalDateTime转Date等
从前面的系列博客中可以看出Jdk8中java.time包中的新的日期时间API类设计的很好,但Date由于使用仍非常广泛,这就涉及到Date转LocalDateTime,LocalDateTime转D ...
- Java日期时间API系列19-----Jdk8中java.time包中的新的日期时间API类,ZonedDateTime与ZoneId和LocalDateTime的关系,ZonedDateTime格式化和时区转换等。
通过Java日期时间API系列6-----Jdk8中java.time包中的新的日期时间API类中时间范围示意图:可以很清晰的看出ZonedDateTime相当于LocalDateTime+ZoneI ...
- Java日期时间API系列8-----Jdk8中java.time包中的新的日期时间API类的LocalDate源码分析
目录 0.前言 1.TemporalAccessor源码 2.Temporal源码 3.TemporalAdjuster源码 4.ChronoLocalDate源码 5.LocalDate源码 6.总 ...
- Java日期时间API系列11-----Jdk8中java.time包中的新的日期时间API类,使用java8日期时间API重写农历LunarDate
通过Java日期时间API系列7-----Jdk8中java.time包中的新的日期时间API类的优点,java8具有很多优点,现在网上查到的农历转换工具类都是基于jdk7及以前的类写的,下面使用ja ...
- Java日期时间API系列12-----Jdk8中java.time包中的新的日期时间API类,日期格式化,常用日期格式大全
通过Java日期时间API系列10-----Jdk8中java.time包中的新的日期时间API类的DateTimeFormatter, 可以看出java8的DateTimeFormatter完美解决 ...
- WWDC 2013 Session笔记 - iOS7中的多任务
这是我的WWDC2013系列笔记中的一篇,完整的笔记列表请参看这篇总览.本文仅作为个人记录使用,也欢迎在许可协议范围内转载或使用,但是还烦请保留原文链接,谢谢您的理解合作.如果您觉得本站对您能有帮助, ...
- ios7中的多任务
转自:http://onevcat.com/2013/08/ios7-background-multitask/ WWDC 2013 Session笔记 - iOS7中的多任务 iOS7的后台多任务特 ...
随机推荐
- Java基础-设计模式之-代理模式Proxy
代理模式是对象的结构模式.代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用. 代理模式是常用的Java 设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理 ...
- CSS布局自适应高度解决方法
这是一个比较典型的三行二列布局,每列高度(事先并不能确定哪列的高度)的相同,是每个设计师追求的目标,按一般的做法,大多采用背景图填充.加JS脚本的方法使列的高度相同,本文要介绍的是采用容器溢出部分隐藏 ...
- 粒子群优化算法-python实现
PSOIndividual.py import numpy as np import ObjFunction import copy class PSOIndividual: ''' individu ...
- JSON后端页面解析
json-lib 请求: http://localhost:8080/MyWeb/pay?cmd=getUrl¶m={"OrderId":"sddd111 ...
- JSP业务逻辑层
经典的三层架构:表示层.业务逻辑层和数据访问层 具体的区分方法 1:数据访问层:主要看你的数据层里面有没有包含逻辑处理,实际上他的各个函数主要完成各个对数据文件的操作.而不必管其他操作. 2:业务逻辑 ...
- mysql 时间函数
select UNIX_TIMESTAMP(Now());#获取unix时间戳1436430994 ::"时间形式
- redis设置密码和主从复制
redis设置连接密码: 因为redis的速度相当的快,在一台比较好的服务器下,每秒可进行150K次密码尝试,所以要设置个非常强大的密码. 设置方式:修改配置文件redis.conf #require ...
- javascript console 函数详解 js开发调试的利器
Console 是用于显示 JS和 DOM 对象信息的单独窗口.并且向 JS 中注入1个 console 对象,使用该对象 可以输出信息到 Console 窗口中. 使用 alert 不是一样可以显示 ...
- 下载安装resin-3.X服务器并配置到myeclipse
前提是先安装jdk,具体自己安装. 1.到resin官网http://www.caucho.com/download/下载相应压缩包,比如resin-3.2.0.zip 2.解压下载的resin-3. ...
- 用 Java 实现断点续传 (HTTP)
断点续传的原理 其实断点续传的原理很简单,就是在 Http 的请求上和一般的下载有所不同而已. 打个比方,浏览器请求服务器上的一个文时,所发出的请求如下: 假设服务器域名为 wwww.sjtu.edu ...