Kill应用之后断点续传的实现

原文链接:http://blog.csdn.net/mumubumaopao/article/details/52076237

Kill应用之后断点续传的实现

之前使用NSURLSession做了一个断点续传的demo,主要实现了在下载的过程中中断下载,然后可以再次启动延续上次的下载链接继续下载的功能.原理是将task的方法cancelByProducingResumeData的Block块中的resumeData获取下来,当再次下载的时候,通过session的downloadTaskWithResumeData方法使用该resumeData创建一个新的task,然后启动下载,就实现了断点续传的功能.但是如果说当前任务正在下载,程序切到后台之后被kill掉,当再次启动应用的时候,就无法继续上次的下载,也就是说,刚才的那种思路,只是适用于用户手动暂停在程序不退出的情况下实现的断点续传,如果应用直接终结则不会继续下载,也就是说并不是真正意义上的断点续传,因为再次启动应用的时候,仍然要重新下载.

于是我就在考虑,如何能够实现当应用意外退出的时候,再次启动应用,仍然可以继续上次的下载任务.我们知道,resumenData中保存的数据是当前任务的下载信息,将其反序列化出来成为字符串的格式输出的话,其内容如下所示(部分冗余内容已经切除,重要信息已添加注释):

NSURLSessionDownloadURLhttp://oarbi0614.bkt.clouddn.com/%E5%86%B0%E6%B2%B3%E4%B8%96%E7%BA%AA.mp4请求的地址NSURLSessionResumeBytesReceived12038723当前下载的文件大小NSURLSessionResumeCurrentRequestYnBsaXN0MDD.....NSURLSessionResumeEntityTag"ljECR82nRMhHvP8D5M9sGQuKBjgK"NSURLSessionResumeInfoTempFileNameCFNetworkDownload_6XdKfZ.tmp下载使用的临时文件名NSURLSessionResumeInfoVersion2NSURLSessionResumeOriginalRequestYnBsaXN0MDD...NSURLSessionResumeServerDownloadDateMon, 25 Jul 2016 10:42:23 GMT

这个数据块里保存了当前下载的的状态,包括临时文件的名字以及当前下载的文件大小.当文件正在下载的时候,会在tmp文件夹内生成一个.tmp文件,保存了当前实际下载的数据,当通过session的downloadTaskWithResumeData方法使用该resumeData创建一个新的task,然后启动下载的时候,task会通过该数据块找到这个文件,然后继续下载.这样一来,貌似我们只需要将resumeData数据块保存下来并且保存存储数据的.tmp文件就好了,当应用意外退出再次开启下载任务的时候,我们只需要使用resumeData创建下载任务,然后将.tmp文件放在tmp文件夹下就好了.

那么现在问题到了如何在程序意外退出的时候如何保存resumeData数据块和.tmp文件上了.我们该如何实现呢?

方法一:当程序意外退出的时候,当前的控制器会调用-(void)viewWillDisappear:(BOOL)animated,应用的代理会调用-applicationWillTerminate:(UIApplication *)application,我们可以在这两个方法里做文章.

在-(void)viewWillDisappear:(BOOL)animated或者-applicationWillTerminate:(UIApplication *)application方法里将resumeData保存到cache文件夹中

要取出resuemData,必然是要通过

__weaktypeof(self)weakSelf =self;[self.downloadTaskcancelByProducingResumeData:^(NSData * _Nullable resumeData) {//在这里可以获取到resumeDataweakSelf.resumeData= resumeData;          }];

这个方法来获取resumeData,但是在实际使用的时候,却遇到了这样的问题,无论是将该代码放在-(void)viewWillDisappear:(BOOL)animated还是-applicationWillTerminate:(UIApplication *)application方法里,weakSelf.resumeData里始终是空的,经过试验,当程序运行的时候,该block块里的代码是不走的,但是在该Block块外的代码,全部都是执行的.我们都知道,当创建session的时候,如果设置queue的时候传入参数为nil,那么他的代理方法都是在全局并发队列里完成的,难道是因为这个原因么?是不是当程序意外退出的时候,这两个方法里的子线程代码都不会执行?我做了如下的实验:

在这两个方法里添加如下代码dispatch_async(dispatch_get_global_queue(0,0), ^{for(inti =0; i <100; i ++){NSLog(@"%d",i);    }});

当在后台kill掉应用的时候,有时候输出0 ,但是如果把下面的代码添加到该方法里,里面的代码仍然是不执行的

dispatch_async(dispatch_get_main_queue(), ^{for(inti =0; i <100; i ++){NSLog(@"%d",i);    }    });

但是如果只把

for(inti=0;i<100;i++){

NSLog(@"%d",i);

}

这几行代码放在这两个方法中,则都是可以完整的执行的,令人百思不得其解.

但是思考一下可以发现:

- 当我们设置session为主线程的时候,经过检查可以发现cancelByProducingResumeData方法里的Block块是在主线程运行的;

- 当我们设置session线程为nil的时候,其代理方法是在全局并发队列里执行的,包括cancelByProducingResumeData方法里的Block块也是在子线程中运行的.

所以我们可以推测一下,在OC底层,cancelByProducingResumeData方法很有可能也是像dispatch_async方法一样是通过向线程队列里添加任务来获得执行机会的.加上上面的三个实验,我猜测是当程序终结的时候,只会执行主线程中的代码,此时如果再通过获取线程向主线程添加任务的话,那么该任务就不会添加到主线程队列里去,更别提往子线程里添加任务了.OC是编译型语言,当应用终结的时候,只有那些写到主线程上面的,编译好的代码在程序终结的时候可以得到执行,而在终结的时候动态添加的任务则无法添加成功,所以在这两个方法里,当程序意外终结的时候,是不能够获取到resumeData的.

方法二:动态保存

既然不能在程序终结的时候获取到resumeData,那么只能在下载的过程中动态的保存了.当应用在下载的时候,会频繁的调用其代理方法:

-(void)URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask*)downloadTask      didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWrittentotalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{    }}

每当有数据过来的时候,都会调用这个方法,经过试验可以发现这个方法的调用频率是很高的,尤其是在全局并发队列里执行的时候,根据测试,该方法的调用频率达到了1200次以上,所以如果每次下载都进行保存的话,那么将会大大的影响应用的效率,因为文件流的输入输出本身就很占用性能,再加上如此高频率的调用,对性能的影响是不敢想像的.所以我做了这样的设计,每当下载文件的十分之一时,获取resumeData.

那么获取resumeData的问题解决了,接下来就是获取.tmp文件了.如果我们要对.tmp文件进行操作,那么就必须要获取到该文件完整的文件名,因为tmp文件夹下绝大多数文件都是.tmp结尾的,而且每次下载产生的.tmp文件都是不同的,所以如果将所有.tmp文件都保存的话,肯定是很不合理的.这时候,就用到了我们获取到的resumeData,因为经过反序列化我们可以知道,resumeData中是保存着当前下载的临时文件名的,所以我们可以对resumeData解析之后,取出其中的临时文件名,而且当下载的时候,其肯定是放在tmp文件夹下的,有了这些东西,我们就可以对.tmp文件进行保存了.

最终采用的方法流程:

由方法二我们可以获取到resumeData和.tmp文件,有了这两个文件,我们就可以在应用下次启动的时候继续上次未完成的下载,在文件每下载十分之一的时候保存一次.具体操作如下:

在session下载代理方法里检测文件下载过程,每当下载超过十分之一的时候,获取resumeData数据块和.tmp文件的路径,然后将resumeData写入到Cache文件夹下,将.tmp文件拷贝至Cache文件夹下,同时在Cache文件夹下建立一个plist文件,key值为.tmp文件的文件名,value值是一个Bool值,标记该文件是否下载完成,相当于该plist文件是管理下载文件的目录;

当应用再次启动的时候,首先从Cache文件夹下读取下载目录,查找未下载完成的文件,找到后将以该文件命名的.tmp临时文件复制到tmp文件夹中,然后取出”Resume_” + 该文件名 命名的上次保存的resumeData数据,使用该数据创建task并开启下载.

备注:Caches文件夹是苹果为用户提供的缓存路径,应用重启该目录不会清考,tmp文件夹会清空,而Documents目录下的文件备份的时候被上传到iCloud并且很快就用完有限的空间,所以我们选择在cache文件夹下缓存我们需要的文件.

经过这样的处理,基本就实现了当程序意外退出的时候,再次启动仍然可以继续上次未完成的下载.不过对性能有些许影响而且不会百分之百续传上次未下载完的数据,会丢失一点点(因为并不是即时保存的).如果使用FMDB或者其他的数据库来管理缓存的文件的话,效果会更好些,不过这次想自己动手写这些东西,就没有用.

这个功能我写了一个Demo,当下载好再次启动的时候,会直接播放本地文件,没有下载完会继续下载,若没有缓存数据则会重新下载.Demo里包括了我自己封装的一个基本的播放器和进度指示器.缓存的功能我写成了一个单例放在了工程里,方便给各位看官查看.这个Demo放在了GitHub上,地址为https://github.com/TheRuningAnt/KillAppDownload.git,欢迎下载查看.

如果各位有更好的思路或者建议的话,跪求指点,多谢!

加入审核被拒交流群,一起交流审核上架经验吧~~ 群号:689757099

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容