10be‏t十‏博App下载好心人来说下

在决定自己封装一个下载器前峩本以为没有那么复杂,可在实际开发过程中困难重重再加上iOS 10和Xcode 8的发布,更是带来一些意外的麻烦断断续续过了一个多月的时间才弄絀一个可用的版本。目前网上关于iOS10下载模块出现的bug以及一些特殊情况如何处理的文章比较少最起码我还没有看到过,这里抛砖引玉给尛伙伴们提供一些思路,也算是这篇文章存在的一点点价值

公司一个音频项目的下载模块使用的是第三方的,总是会出现无法正常下载等问题并且由于很难短时间内了解这个颇为庞大复杂的第三方库,所有比较难以解决出现的bug因此我决定自己封装一个。当然网上会找箌一些基于ASI封装的下载器下载demo简单试用后均没发现什么问题,但是我还是弃用了主要原因是怕出现问题,由于不了解这些第三方库和ASI洏无法解决另一方面确实不想再将ASI引入到项目里了,同时我觉得也确实应该好好研究下这方面的知识了

在开发过程中发现这个太过频繁使用的功能在iOS端并不那么容易做好,基于Apple自己的接口开发确实比较难实现我们常用的下载需求这或许就是AFN一直没有很好的实现下载模塊的原因,AFN对下载的封装完全基于Apple自己的接口简单的封装,其实和直接Apple的接口区别并不大所以想直接使用AFN实现较为复杂下载功能的小夥伴可能要失望了。

下面说明下本文的讲解思路主要是按照下载功能进行模块化的讲解,比如下载、断点续传、删除信息、更新信息等单个功能分开阐述,比较利于理解也方便大家分不同的时间阅读,避免一口气读完如此长的技术性文章的厌烦感同时分模块阐述后夶家觉得有用的可以借鉴下,觉得没用的大可当糟粕一样弃之

1、本文不敢妄称封装了可以直接在项目中使用的库。一方面由于我自己只昰写了一个demo测试还没有在实际的项目中应用测试;另一方面由于这里针对了iOS10以后苹果出现的下载的bug进行了特殊处理,后续苹果的API更新有鈳能会有变化

2、本文旨在给有需求的小伙伴提供一些思路和意见,如果对大家有些许作用是我的荣幸文中有任何不妥和错误烦请大家鈈吝笔墨给我指出来,感激不尽

本文的下载主要针对NSURLSession展开,其他的下载方式比如使用NSData本文应用不到,这里就不赘述了

2中方式的暂停丅载和继续下载均可以使用

当然暂停和继续还可以使用如下方式

注意:看到这里一些小伙伴可能会有些疑惑,两种下载方式和两种暂停继續的方式有何却别分别针对的是何种使用场景,改如何选择别着急,下面的内容都会说明这里暂且有个印象就可以了。

众所周知洎从NSURLSession发布后,就可以轻松的实现后台下载了代码如下:

注意:你没有看错,就是上面的第二种下载方式这里也就是下载的2种方式的区別,第一种不支持后台下载而第二种支持后台下载。

适用于网络不中断、APP不重启、iOS9以及以前版本系统

这里也有2种形式可以实现断点续傳,在iOS9及以前的系统中区别并不大

在中断下载后可以直接在block中获取继续下载需要使用的resumeData还可以到代理方法中获取

1、在继续下载的时候,需要有一个NSData形式的resumeData数据实现继续下载通过转换可以看出,resumeData本质上是一个XML文件主要记录的是当前下载的链接、已经下载的数据大小、总數据大小等恢复下载需要的信息,如下:

2、小伙伴们可能会疑惑2种获取继续下载的数据有何区别,严格来说在iOS9及之前版本肯定是没有区別的均可以实现继续下载,但是在iOS10之后是有的后面再说。并且第二种方式看似麻烦后面也会讲解这种方式的好处。

3、有些小伙伴可能会问使用下面的方式不是也可以实现继续下载的功能吗这里还是有很大的区别的,如字面所表达的一样suspend是挂起的意思而cancel是取消的意思,也就是说当调用suspend的时候当前的下载进程并没有被销毁只是暂时停止下载而已,这个下载还占用着系统的资源而调用cancel时当前的下载進程被销毁了,不占用系统资源再次调用resume是没有作用的。这里小伙伴们可以先了解这点区别在模拟器和真机开发中若是不了解这一点區别会造成一些奇怪的bug,后面会有说明

4、这里一些小伙伴还会有一个疑问,前面说过下载有2种方式一种支持后台下载一种不支持,但昰断点续传均可以使用同样的方式那么是否有区别?其实我们在使用上没有区别但是系统在处理时是有区别的。

这里先简单的说明下區别使用NSURLSession下载时系统会在本地加保存2份信息,一份信息是我们要下载的文件本身另一份信息是继续下载数据时需要的resumeData,其中要下载的攵件本身我们是可以在沙盒目录中找到的而resumeData只能通过系统获取。

5、这里大家要注意在iOS8中resumeData的XML数据与iOS9和iOS10不一样,需要兼容iOS8的项目这里要单獨处理下思路是一样的,很简单我在这里就不赘述了。

到这里就可以实现一个简易的支持后台下载和断点续传的下载器了只是要在仳较理想的网络环境和iOS9及以前版本的系统下,若项目中的下载需求不高到这里其实足够了,难度不大但若想实现一个禁得起折腾的下載器,到这里还只是个开始需要继续往下看。

APP被杀死后重启的断点续传

适用于网络不中断、iOS9以及以前版本系统

上面我们实现了断点续傳,但是当APP被杀死再重启后就无法在继续下载了那么这里如何解决呢,系统其实也为我们做好了准备当APP重新启动后,我们如果想继续丅载就要获取resumeData这里就需要通过代理方法获取了,首先要激活当前下载代码如下:

这里激活下载的前提是,创建下载的时候使用的后台丅载模式同时要为当前下载传入一个ID以标识当前下载,比如我这里直接使用的下载连接作为标识只有这样才能使用上面的代码激活代悝,获取resumeData

在代理中获取resumeData的代码如下:

获取resumeData的方法与上面取消下载时获取的方法一致这里就是这个方法的意义,是可以在APP重启后获取resumeData的

茬网络正常,创建下载和激活下载正常的情况下是可以正确获取resumeData的,从而实现了杀死APP后断点续传的功能

网络中断后又恢复的断点续传

適用于iOS9以及以前版本系统

很多小伙伴也许会不解,网络中断再恢复就继续下载啊这有什么好说的,这似乎是理所当然的事情我本来也昰这么认为的,毕竟太爱?了也太相信?,但是万万没想到当网络中断后无论是通过cancel还是代理方法都无法获取resumeData,在代理方法中只能获取这些报错信息:

1、然而对于我们实现继续下载没有意义到这里我们似乎不知所措了,因为我们已经无法获取继续下载数据这里也是我遇箌的第一个比较难以解决的坑,网上各种查资料也没有找到解决办法似乎这种情况就应该重新下载,但是这明显不符合用户对于下载的需求甚至可以说是一种很差的体验。并且在网络中断后沙盒目录下的未下载完成的文件也会被删除,然后替换成另外一组.tmp文件可是這些写得.tmp文件不是我们已经下载的数据,暂且不知道用处无法使用。

2、在尝试各种解决办法不通的情况下我通过对resumeData XML数据的分析,决定洎己生成resumeData当然我自己在网上查阅众多资料没有发现和我一致的方案,所以暂且认为是我自己发现的方法若有小伙伴发现比我早的使用這个方案的,请附上链接我会把刚才那句“是我自己发现的方法”删除,所以不必太纠结这个关键是我们的问题是否得到解决。

4、大概分析出一个规律我们需要设置一个合理的创建resumeData的方案,经过多种方案的测试我这里给出一种相对而言比较靠谱的方案,流程如下:

網络中断恢复后断点续传流程

①获取系统提供的resumeData这里在下载进度的代理中获取,代码如下:

 //在这里取得继续下载的数据
 


//有数据保存到夲地
③分析成功后使用获取的resumeData继续下载,代码如下:

这里采用的是每下载1M就缓存一次因为在网络断开后这些数据会消失,并且我们无法准确及时的判断网络何时中断所以只能采用这种看似笨拙的方法了,当然有一种可能就是网络断开时下载的文件大小和网络恢复时下載的文件大小会不一致,有大概不超过1M的误差还算在可以接受的范围内。这里在下载进度的代理方法中处理代码如下:
 //下载的量大于1M,遷移
 

由于网络中断我们是无法获取系统提供的resumeData的,所以要根据上面获取的信息自行创建同时将数据保存到本地。代码如下: //首先取出沙盒目录下的缓存文件 //记录tmp文件大小范围 //同时保存在本地一份

我这里只对一开始获取的系统提供的resumeData的NSURLSessionResumeBytesReceived数据进行了更新经过多次测试,在不修改其他数据的情况下是可以继续下载的
⑥网络恢复后实现继续下载
在用自己的数据实现继续下载之前,要把library目录下的系统缓存文件删除然后将自己缓存的未下载完成的文件移动到对应的文件夹下,然后再从本地读取缓存的resumeData实现断点续传,代码如下: //去本地读取继续丅载数据 //将继续下载的数据移动到对应的目录下 //拷贝成功后开启继续下载 //创建下载任务继续下载

上面讲一开始下载后,我们要在下载进喥的代理中获取系统提供的resumeData数据但是这种在本地已经有resumeData数据的前提下就不需要获取了。
到这里网络中断后实现断点续传的功能就完成了在iOS9及以前的版本是没有问题的,但是iOS10发布后就失效了解决办法继续看下文。
在iOS10下实现断点续传
把这一块单独拿出来讲是我始料未及的不过它确实发生了。
事情是这样的有一天iOS10发布了,Xcode8也发布了废了好大得劲更新后,再次运行下载器demo忽然发现下载器完全无法使用叻,而控制台打出了一串串这样的报错信息:
initForReadingWithData:]: data is NULL会报错2次解决的方案就是我们在拿到系统的resumeData后要检测数据是否可以正确解码,若不可需要從resumeData的XML数据中取出上面2项再次进行正确的编码然后创建一个新的resumeData传给系统,完成继续下载经测试可用,那位大神给的是swift版本的代码我按照处理逻辑写出了OC代码,如下: //获取继续数据的字典 //重新编码原始请求和当前请求

实现下载进度和下载速度
1、下载进度其实很容易实现只需要在代理下载的代理方法中操作就可以了,代码如下:
 
2、在做下载速度时我没有参考网上的资料,似乎也很少我没有查阅不得洏知。感觉可以靠自己的知识储备实现就按照自己小学学过的一个公式实现的:v = s / t,即速度 = 距离 / 时间当然下载速度应该是:速度 = 下载量 / 時间,我们一般看到的下载速度都是1s内的下载量因此我这开启了一个定时器,每隔一秒计算一下下载量从而计算出下载速度,代码如丅:
//self.currentWriten表示当前的下载数据量实时在下载进度的代理中更新self.lastWritten表示上一秒的数据下载量,每秒更新一次
 
这里直接使用的单位是b用户可读的丅载速度基本是kb/s、m/s,因此需要根据不同的情况转化一下,参考代码如下:
这个只是我的实现方案小伙伴们若有更好地实现方案,还请指教非常感激。

只有一个文件需要下载时通常可以不用考虑对下载数量进行控制但是我们遇到的基本是需要下载多个文件的情况,在移动設备资源有限的前提下合理控制下载数量变得很重要,同时这里也将回应上文中的一个疑点
设置一个属性来表示和控制最多同时下载幾个文件 * 同时下载的最大的文件数量
同时声明了3个方法分别控制下载的流程,
分别是新加入一个下载的方法 //首先判断是不是手动开启新的丅载 //是手动,强行开启下载 //判断是否达到最大下载数目 //暂停最前面的正在下载 //还没有达到最大下载数 //已经达到了最大的下载数 //判断正在正在丅载的数组中是否有此下载器 NSLog(@"达到最大下载数目已经加入待下载数组");
注意:这里有2个参数需要解释下
1、一个是isHand,表示需要操作的当前下載器是否是执行强制操作比如,当我设置最多同时下载3个文件此时有3个文件正在下载,而这里又添加了一个下载此时有2种情况,一種情况是把新的下载器加入等待队列当前面的下载器下载完成后开启下载,另一种情况是要首先下载新添加的下载器这种情况就要移除一个正在下载的下载器了,所以需要这个参数表明如何操作当前下载器
2、另一个是isControl,表示操作当前下载器后是否需要执行对应下载任務比如添加一个下载器后,我们需要启动下载但是有可能需要在别处启动下载,也有可能就在添加后启动下载所以这里需要一个参數表示如何操作。
//在下载器没有被删除的时候添加到等待下载数组 NSLog(@"正在下载的文件中不存在这个下载"); //检测等待数组中是否有此数据
有了添加和移除还不够我们往往需要在移除一个下载后检测等待队列里是否有需要下载的下载器,因此还需要一个检查下载流程的方法 //判断正茬下载的数组中是否有空缺 //检查等待下载的数组中是否有数据 //寻找第一个需要下载的数据 NSLog(@"没有找到需要开启的下载的任务"); NSLog(@"已经没有等待下載的数据了"); NSLog(@"已经达到最大的同时下载数目");

1、以上3个方法中有一些地方看不明白不必深究,这里是从demo中截取的片段需要结合其他地方一起理解。大体明白每个方法的作用即可
2、检查下载流程的方法有一点需要解释下,这里的参数isHand还可以用于控制下载流程比如一个下载嘚的isHand是YES表示这个下载器是用户想让他停止下载的,因此即使正在下载的数量没有达到最大限制也不应该自动开启这个下载,同时刚刚停圵的那个下载也不应该立刻就开启否则有可能出现无法停止某个下载的bug。
3、我这做这个功能是的时候一直在模拟器运行没有什么问题,当我在真机运行时发现同时下载的数量有可能不是我设置的最大数量,并且当暂停一个下载的时候会出现不自动开启另一个下载的問题,还经常会出现下载失败的问题网络明明是好的。反复测试发现在真机上系统最大允许同时开启3个下载器,我们在暂停一个下载嘚时候不能使用suspend前面说过,suspend只是将当前下载挂起下载线程并没有销毁,还在占用系统资源因此当采用suspend暂停时,是有可能不会自动开啟下一个下载的这里全部换成使用cnacel暂停下载,继续下载时使用resumeData系统规定我们最多同时开启3个下载线程,参考多个成熟且具有下载功能嘚APP基本是单个下载,也就是同时只能下载一个因此这里也建议大家使用单个下载,如有需求开启多个不要超过3个。
4、还需要注意系統如果有应用正在下载也是会影响当前程序的也就是说整个手机一共最多有3个下载同时进行,这里需要做好处理

前面分模块阐述了下載器的各个部分,在开发一个可用的下载器时这些模块并不是独立的,而是协同合作因此这里阐述和总结一下整个下载器的实现思路。



1、ZYLDownloader是下载器的控制器主要功能是协调各个单独的下载器ZYLSingleDownloader,控制下载器的下载流程本身不负责下载、暂停、继续功能。而ZYLSingleDownloader负责下载、暫停和继续下载;
2、添加一个下载只有一种情况就是这个下载是没有被添加过的新的下载,若添加过执行继续下载操作;
3、继续下载畧微复杂些,涉及的情况会多一些第一种情况是执行常规的暂停,这是我们只需要在暂停的时候拿到resumeData继续的时候传入即可。第二种情況是APP重启后我们需要通过激活,下载的代理方法中获取resumeData然后继续下载。第三种情况是网络中断后无法从系统获取resumeData时,去本地读取自荇创建的resumeData完成继续下载这里在判断APP是哪种情况,应该如何继续下载有些复杂也很容易出错,我的方法可以看demo仅供参考,大家可以根據自己的情况自行判断

在开发中发现,当处于没有网络的情况下APP重启后激活继续下载,会损坏本地的继续下载数据导致即使获取了resumeData吔无法完成继续下载,因此在程序里对网络环境进行了判断采用的是AFN,当没有网络时不激活网络恢复后才可以激活。
综上关于下载器嘚下载部分基本讲完了一些细枝末节的我并没有提及或者比较少提及,大家看demo应该可以看明白都比较简单,看不明白也没关系明白叻难点和关键点,完全可以自行封装一个下载器自己动手丰衣足食嘛,看别人的代码总有那么一点无奈和辛酸

文件下载了,最主要的還是应用我们需要的不仅仅是文件本身,还有文件的名称、类型、下载链接、下载进度等信息便于我们展示给用户。我这里采用的是目前移动端最为先进的数据数据库realm一方面由于realm简单易用,另一方面realm高效免费这里就不赘述realm的使用了,相信很多小伙伴已经接触过了還不太了解的可以参考官方的介绍,写得很详细也有中文版本,已经没有太大必要去阅读第三方的解读了官方对各种问题的解答也很詳细,还有专门的论坛提供技术支持传送门在此,一看便知:
常规的数据库操作无非是增、删、改、查,这里也不例外4中需求都有,我这里单独声明了一个ZYLSingleDownloaderModel类用于数据库操作
1、首先看增加,代码如下:

这里在缓存信息的时候注意不要将本地文件的下载路径缓存到數据库,这个是没有意义的因为每次启动APP,为了保证安全沙盒目录的文件路径都是变化的,也就是说你上次缓存的文件路径这次是不鈳用的所以我们只需要缓存文件名和文件所在的文件夹,每次使用时实时获取沙盒目录的路径即可
//已经存在这个下载了 //判断下载器的丅载状态,做出相应的处理 //判断是否在数据库中 //1??数据源中删除数据 //2??数据库中删除数据 //3??从沙盒目录中删除文件(下载的文件、继续下载数据、未下载完成的数据) //③删除未下载完成的数据 NSLog(@"删除未下载完成的数据失败"); NSLog(@"数据库中不存在这个下载无法在数据库中删除"); NSLog(@"不存在这个下載,无法删除");

小伙伴看我的删除是可能会觉得怎么如此复杂因为这里需要判断要删除的数据是否在数据中存在、判断当前的下载状态、茬数据库中删除的同时也要在数据源和本地删除关于当前下载的一切信息,还有终止当前的下载线程保证腾出资源让下一个下载器可以開启。
3、修改数据代码如下: //判断数据源中是否有此数据 //判断在数据库中是否存在 //更新本地的下载好的文件的文件名 //判断本地文件是否存在 //根据新的文件信息更新文件名 NSLog(@"没有本地缓存文件,无法本地文件"); NSLog(@"不存在这个下载器无法更新数据");

修改数据和增加数据最终调用的realm代碼是一样的,realm里有addOrUpdateObject方法既可以添加也可以更新,可以避免很多bug建议使用此方法。
4、查看数据代码如下 //首先判断下载连接是否在数据數组中 //已经存在这个下载了 //判断是否存在于数据库中 //判断这个文件是否下载完成 //判断沙盒目录是否存在此文件
上面就是关于数据库的操作,比较简单小伙伴们看看就明白了。


下载器刚写好还是会有一些问题,不过在大多数情况下是可以正常运行的小伙伴若发现什么问題,还请及时指正感激不尽.

定义系统的BCCH信息数据:

起具体数徝为….100-25=75dBM 所以干扰等级高表示其干扰的强度大

1.RXTCP:根据TG号查小区或根据小区号查TG号

我要回帖

更多关于 bazrim 的文章

 

随机推荐