首先,后台下载和断点续传是两件事;这里放在一起说是为了图个方便,实际二者在技术实现上没有什么关联。
目前我们的下载实现一般都是基于nsurlsession和iOS7+的,所以我们这里不考虑iOS6和以前的老系统,主要技术实现也是基于nsurlsession.
先说后台下载:
1. 在没有特别关注的情况下,可能很多开发者使用afnetworking下载的姿势并没有考虑到后台下载这一块。在默认情况(不做特别设置)下,afnetworking并未启用backgroundsession, 因此很可能你的app是不支持后台下载的。
2. nsurlsession对后台下载的支持主要通过 backgroundSessionConfiguration/backgroundSessionConfigurationWithIdentifier 类型的session来支持;通过该session来调用下载方法,实现几个下载相关的回调方法,也就可以实现基本的目的:应用切换到后台时仍然在下载(但下载完成后得不到回调和提醒)。
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location;
@optional
/* Sent periodically to notify the delegate of download progress. */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;
/* Sent when a download has been resumed. If a download failed with an
* error, the -userInfo dictionary of the error will contain an
* NSURLSessionDownloadTaskResumeData key, whose value is the resume
* data.
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes;
3. 如果需要下载完成后得到回调和提醒,或者更高级一点(应用程序被系统干掉等情况,还能够通过下载任务拉起并发送回调和提醒,而且系统在后台会生成一个快照,按home键查看时就仿佛这个应用被启动过一样。这里的内容是根据苹果官方答复和相关文档得到,系统干掉的场景笔者并未模拟),我们就需要进行额外的设置了。
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {
// 这里返回了session的identifier和completionBlock;其中的identifier可以用于匹配session,而completionBlock我们需要保存起来,到后面一个步骤需要调用之。
}
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
// 这里需要执行上一步保存的completionBlock:
根据苹果员工的回复,这里苹果需要用这个block实现两个功能:
a. 系统后台生成快照
b. 释放阻止应用挂起的断言(让应用继续在后台运行,节约系统资源)
-
The system does two things in that completion handler:
It snapshots your UI for the benefit of the multitasking switcher
It releases the assertion that was preventing your app from being suspended
https://forums.developer.apple.com/thread/69825
}
在一次后台下载中方法的执行时序为:a.handleEventsForBackgroundURLSession ->b. URLSession: downloadTask: didFinishDownloadingToURL->c. URLSessionDidFinishEventsForBackgroundURLSession.
建议发送本地通知的动作统一放在b中,目前看放在a和c中应该也是可以的。
再来说一说断点续传:
1. 从协议层面来说,断点续传的实现都是通过http头里面的range来实现的。对于以前asi的实现和基于nsurlconnection的实现来说,我们都是通过手动保存当前大小然后填充到range的策略来实现断点续传功能的。
2. nsurlsession内置了对于断点续传的api支持,主要是通过resumedata和tmp中间文件。在nsurlsession暂停时,其中间文件的相关信息被保存到resumedata,我们只要通过这个resumedata,即可从之前的下载进度中恢复。也就是说1中的细节被封装在api内部了。
目前来看,基于nsurlsession的断点续传主要有两个问题需要考虑:
a. 程序退出(手动kill等)时需要自动保存resumedata,后续应用起来时再恢复之。
b. iOS10里面的resumedata的保存有点问题,需要特别处理,可以参考此demo:https://github.com/HustHank/BackgroundDownloadDemo.
其他细节暂时不再展开了,后面有机会再深入的说一说,大体方案就是这样,大家可以尽量参考苹果官方文档。
一些参考文档:
https://my.oschina.net/iOSliuhui/blog/469276 断点下载流程讲解
http://www.cocoachina.com/ios/20160503/16053.html 断点续传方案 ??? 不断保存?方案不可取! 程序被杀时主动保存!这里的ios应该为小写,被自动格式化成大写了
https://code.tutsplus.com/tutorials/ios-7-sdk-background-transfer-service--mobile-20595 后台传输服务
http://www.jianshu.com/p/1211cf99dfc3 下载相关,BackgroundDownloadDemo作者的分享
苹果论坛相关讨论
https://forums.developer.apple.com/thread/14854
https://forums.developer.apple.com/thread/69825