实现项目下载需求时遇过的那些坑
作者:网络转载 发布时间:[ 2015/9/10 11:02:58 ] 推荐标签:软件测试管理
导语
当前市面上的APP,凡有涉及到视频、期刊、或其它大型文件传输、浏览等用途的,添加下载或缓存至本地的功能以避免网速的限制及依赖,毫无疑问都将给用户带来更好的体验。而谈到下载技术,又不得不牵扯到了断点续传,队列任务等老生常谈的问题。这不,本人当前的项目,恰好遇到了这样的需求。然而在经过大量调研之后,本人竟无法找到一篇总结得很好的文档,对此进行全面的介绍;能够寻到的一些活跃度并不高的开源项目,却又不能恰如其分并抱之以信赖满足项目的需求。所以仔细斟酌后,不得不选择自己动手,丰衣足食。钻研的过程中遇到了不少坑、不少困难,有些个别的地方真是不用不知道,一用才知道是如此得蹩脚,难怪很少有人对此进行系统完整的介绍。现将本人在实现下载模块时所用到的技术总结如下,相信本文中所蕴涵的干货一定不会令费心阅读的你感到失望!
话休絮烦。首先,说下载离不开网络请求。而当今iOS开发技术当中,广泛使用的网络请求框架无疑要属AFNetworking。经过对其进行简单研究后,你会寻到适合用来完成下载这件“小事”的组件,叫做AFHTTPRequestOperation
现假定我们的需求是常见,也是能体现技术问题的一个,叫做:
下载队列在某一时刻,多仅能有一个下载任务处于正在下载的状态中!
-- 叙述的节奏似乎稍稍快了些
那先来看下实现队列下载、断点续传等需求的关键示例代码吧!
NSError * error = nil;
// 创建下载队列
NSOperationQueue * downloadOperationQueue = [[NSOperationQueue alloc]init];
// 规定operationQueue中,大可以同时执行的operation数量为1
downloadOperationQueue.maxConcurrentOperationCount = 1;
// 创建单个下载任务(访问已下载部分的文件,实现断点续传)
NSMutableURLRequest * downloadRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:DOWNLOAD_URL_STRING]];
[[NSURLCache sharedURLCache] removeCachedResponseForRequest:downloadRequest];
AFHTTPRequestOperation * downloadOperation = [[AFHTTPRequestOperation alloc]initWithRequest:downloadRequest];
unsigned long long downloadedPartFileSize = 0;
if ([[NSFileManager defaultManager] fileExistsAtPath:DOWNLOADED_PART_FILE_PATH]) {
NSDictionary * fileAttributes = [[NSFileManager defaultManager]attributesOfItemAtPath:DOWNLOADED_PART_FILE_PATH error:&error];
downloadedPartFileSize = [fileAttributes fileSize];
NSString * headerRangeFieldValue = [NSString stringWithFormat:@"bytes=%llu-", downloadedPartFileSize];
[downloadRequest setValue:headerRangeFieldValue forHTTPHeaderField:@"Range"];
}
downloadOperation.outputStream = [NSOutputStream outputStreamToFileAtPath:DOWNLOADED_PART_FILE_PATH append:YES];
[downloadOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
NSLog(@"%lld/%lld", totalBytesRead + downloadedPartFileSize, totalBytesExpectedToRead + downloadedPartFileSize);
}];
[downloadOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"downloadOperation completion block invoked");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"downloadOperation failure block invoked");
}];
// 将单个下载任务加入到下载队列当中
[downloadOperationQueue addOperation:downloadOperation];
// 暂停某下载任务
[downloadOperation pause];
// 继续某下载任务
[downloadOperation resume];
// 取消某下载任务(同时应将其已下载部分的文件删除)
[downloadOperation cancel];
[[NSFileManager defaultManager] removeItemAtPath:DOWNLOADED_PART_FILE_PATH error:&error];
// 取消全部下载任务
[downloadOperationQueue cancelAllOperations];
// 此外还有若干方法用以判断相应一见其名便知其义的状态...
downloadOperation.isReady
downloadOperation.isExecuting
downloadOperation.isPaused
downloadOperation.isCancelled
downloadOperation.isFinished
// 判断downloadOperation是否存在在downloadOperationQueue当中
[downloadOperationQueue.operations containsObject:downloadOperation]
以上代码创建了一个AFHTTPRequestOperation对象作为单个下载任务,并将其加入到NSOperationQueue中。仿照上例,我们可以创建多个AFHTTPRequestOperation对象并加入到NSOperationQueue中,即形成了下载队列
做到这里,你是不是认为已经没有神马技术问题啦?只要把operation一个个地添加到queue里, 下载任务可以一个接一个地自动执行了!而如果我们将上一个operation暂停、取消,或是它自然地下载完成了,又或是它下载中途失败了,下一operation会聪明地自动启动,继续其下载任务了!!?
错!!!!
接下来笔者将要告诉你的,是本文核心的干货,颠覆你的想象!!
只要你亲手动手试一试,会发现如下大跌眼球的惊恐现象!!
惊人事实 1: 对queue中前一个下载operation执行pause方法,下一个operation并不能自动启动进入正在执行的状态!!
惊人事实 2: 如果queue中前一个下载operation执行失败了(可用下载中途断网进行模拟),它将从queue中自动地被移除掉!!
惊人事实 3: 注意到代码里那个failure回调的block了没?它不仅仅将在operation执行失败的时候被调用,还会在operation被cancel的时候被调用!!所以对于神马叫做“operation的失败”,你要重新建立起你的世界观了!!
惊人事实 4: 如果对一个正处于pause状态的operation执行cancel会怎么样?答案是这个operation还保留在queue中!!并且仍然保持着pause状态!!仅有的一点变化,是它的isCancelled属性,变成了YES!!
......未完待续,本文要令你感到惊诧的,还有很多
由于这些问题间相互关系的错综复杂,为了清晰条理地予以说明,特将本人实验中所观察到operation的行为总结如下表。其中有悖于我们想象的结果,已用彩色背景字体标出
有木有感到AFHTTPRequestOperation和NSOperationQueue是个多么坑爹的东东?为何不能像我们想象中一样用得舒爽?
原因在于AFHTTPRequestOperation的父类NSOperation,在设计之处不是为了下载的操作而生的!人家开始仅仅是用来处理多线程的啊!!所以造成了AFNetworking在扩展这个类的时候,可用的资源、接口等等非常少。对于什么下载任务暂停/继续,下载中途失败等等情况,很多问题几乎是没有办法理想地解决的,只好用NSOperation中仅有的几种状态予以并不贴切的表示。于是乎出现了上表中种种诡异的情况
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11