iOS后台下载和断点续传

2016-12-08

iOS后台下载和断点续传

最近在重新整理我们项目里的iOS的后台下载,因为原来方法(ASIHTTPRequest方式)无法做到后台一直下载,这个问题被我老婆吐槽了好几次。所以我重新整理一下,用NSURLSession来下载,达到了比较好的效果。现在总结自己的一些经验。

背景

项目最开始我们是用了NSURLSession来做后台下载的。但是有两个严重的问题

  • 有时候偶现的不能下载,就是一启动下载就失败。一旦出现这种情况,无法恢复,怎么样都无法下载,所有任务都一样
  • 下载速度很慢,只有几十到几百KB/S,网络正常,Android端同一个文件下载速度飞快
  • 程序杀掉之后无法重新继续下载

按道理这些问题都不应该出现,可是我们的应用就是出现了这些问题,而且难以调试,同事调试了很久都没有结果。最后我也没认真研究,但是我发现ASIHTTPRequest可以很好地解决上面的几个问题,然后我就在原来的基础上加了ASIHTTPRequest的下载方式。

ASIHTTPRequest方式的下载不错,速度很快,也能很好的断点续传,但是跟Android的还是慢了一点,不过我觉得这可能是系统不同的原因。ASIHTTPRequest还有一个比较致命的弱点,就是不能后台下载,这对于下载大文件来说是必须的。在iOS平台要做后台下载,最好的方式还是使用NSURLSession。

所以我决定好好研究一下NSURLSession,并且改用这种方式。

NSURLSession后台下载

实际上NSURLSession的后台下载真的很强大,苹果真做了件很好的事。当你创建一个后台下载任务的时候,实际上你就把这个下载交给系统来接管了。所以即使你把APP杀掉,下载也不会停止,很牛逼。而且如果出现手机网络变化之类的,系统会在后台帮你重试,当网络又正常了它会继续下载。所有估计一旦你启动了后台下载任务,要么下载完,要么你手动取消,要么服务器那边出错,不然这个下载不会停止。

虽然NSURLSession那么强,但是要做一个体验比较好的下载器还需要注意很多地方。

  • 必须是支持后台下载的NSURLSessionConfiguration
  • 必须用这个 (NSURLSession )sessionWithConfiguration:(NSURLSessionConfiguration )configuration delegate:(nullable id )delegate delegateQueue:(nullable NSOperationQueue *)queue来创建NSURLSession,并且delegate不能为空。NSURLSession最好全局唯一
  • 必须实现delegate几个重要的方法
  • 最好实现 APPDelegate中的- (void)application:(UIApplication )application handleEventsForBackgroundURLSession:(NSString )identifier completionHandler:(void (^)())completionHandler方法,并且把completionHandler保存起来适时调用
  • 在手动暂停或者失败后,要把resumeData保存起来,最好是保存的本地,重新启动下载是需要这个来继续下载

还有需要注意的是 在 - (void)URLSession:(NSURLSession )session downloadTask:(NSURLSessionDownloadTask )downloadTask didFinishDownloadingToURL:(NSURL *)location 这个回调方法里,必须location指向的临时文件移动到你的沙盒目录中,因为这个方法一旦返回后,就会去删除这个临时文件。

根据上面这些,我自己做了一个下载任务管理器,可以创建多个任务,并可以配置多个任务并行下载。当一个下载任务完成后便会启动下一个等待中的任务。这样的话,你就可以创建完下载任务后,就关闭程序,该干啥就干啥去。下载完了它会发本地通知。

不过这个下载器在公司的项目里用,牵涉比较多,还支持ASIHTTPRequest下载,所以暂时没法开源。不过有一个demo包含了核心的思想。

demo在这里 BackgroundDownloadDemo

参考资料:

1.WWDC 2013 Session笔记 - iOS7中的多任务

2.基于iOS 10、realm封装的下载器(支持存储读取、断点续传、后台下载、杀死APP重启后的断点续传等功能)

3.iOS使用NSURLSession进行下载(包括后台下载,断点下载)

Category: iOS Tagged: NSURLSession ASIHTTPRequest 后台下载

comments


iOS直接上传系统照片和视频(ALAsset)

2016-12-01

iOS直接上传系统照片和视频(ALAsset)

我之前有一篇文章里讲过上传系统照片和视频的事(iOS系统相册上传不得不说的那些事儿),需要将ALAsset从系统相册里导出到沙盒文件里,然后再将这个文件上传。这里需要无端端写一次文件的时间就不说了,最要命的是还要占据额外的磁盘空间。我们的用户一般要备份相片视频的时候,往往是手机空间不足的时候。这个时候存储空间很紧张,你备份还需要额外的空间。如果一个视频很大,例如2G,那么手机上需要有空闲的2G空间才能导出视频,才能备份。真是硬伤。

这个问题一直在我心头,卡了我很久,之前我有网上找解决方法,没有找到,很多都是说将ALAsset导出文件到沙盒的事。我自己也有想过要怎么解决。我想过从ALAsset中读取一段一段的NSData数据,然后分别将这些数据上传。这样也许是可以的,但貌似需要服务器那边能将这些包组合起来。另外客户端这些做起来也挺复杂。还有一种方法是我不使用AFN库,自己直接用NSURLConnection来写,跟服务器建立连接后不断的从ALAsset中读取数据写入跟服务器建立的连接。这个是参考Java的写法。可是我又不知从何动手。并且之前都很忙,没有时间想这么多。

最近我比较闲了,我想正好有时间来解决这个问题。这是我心中的一块石头,我要把它拿掉。

这次我直接在github上找,说不定能找到一些有用的代码。果真让我找到了 FMAssetStream这个库,这个库的做法很简单,定义了一个子类来扩展NSInputStream,重载了一个最重要的方法 :

- (NSInteger)read ...

Category: iOS Tagged: 上传 ALAsset NSInputStream

comments

Read More

iOS应用2017年强制应用HTTPS问题

2016-10-18

iOS应用2017年使用HTTPS问题

昨天一个朋友发了一篇介绍HTTPS方案文章给我看,文章提到2017年苹果强制所有APP使用HTTPS协议,问我是不是真的,还是说那只是一篇软文。他也很担心,因为他的APP不是使用HTTPS协议。

其实应该说是这样:从2017年1月1日起,苹果强制新提交的APP使用HTTPS协议。这里要强调新提交,也就是说已经上线了的APP不受影响,新提交的APP或新版本提交才受影响。虽说强制,但是还是有方法不使用HTTPS协议的。

我查了下,现在的资料有提到3种方案:

  • 1是APP里有一个配置开关NSAllowsArbitraryLoads,打开这个开关,所有网络请求都可以使用HTTP。现在所有没有用HTTPS协议的都是这么干的。这个开关现在用是没有问题的,但是2017年后苹果就不希望你这么用了。你再用的话,需要说明原因,而且理由不充分的话苹果有可能拒绝通过你的APP。

  • 2是增加域名白名单(NSExceptionDomains),访问白名单之内的网站请求可以是HTTP请求。这种做法也要说明理由,苹果认为你的理由不充分仍然有可能拒绝通过你的APP

  • 当然还有一种方案,是在iOS10系统新加的属性NSAllowsArbitraryLoadsInWebContent,专门针对浏览器访问的开关。设想一个浏览器应用,用户可以输入任务网址,这个网址能不能支持HTTPS协议,没人知道。所以就有这么一个开关,打开之后,浏览器的访问就不受HTTPS限制。但是不好的消息是,这个浏览器必须是WKWebView,而不能是UIWebView(截止2016.10.18前 ...

Category: iOS Tagged: HTTPS 2017 iOS10

comments

Read More

iOS上传文件支持断点续传

2016-09-30

iOS上传文件支持断点续传

在挺久之前我写过一篇文章里提到上传文件的断点续传的问题,我没有找到好的方法。以前我采用的方式是用NSFileHandl的方法seekToFileOffset,移到已经文件已经上传了的部分,然后采用readDataToEndOfFile读取剩下部分到内存中NSData。但是这个方案问题是,如果文件很大,需读取的NSData很大,内存就会爆掉。所以最终没有采用这个方案。

最近我们来个新同事,技术能力很不错。我让他去研究一下这个问题。开始他找到的方案跟我之前的那个是一样的,我说这个我之前有考虑过,不能采用。然后他继续研究,后来发现了原来NSInputStream有相关按offset读取文件的接口。不,正确来说是NSStream的接口,而且有点隐蔽性质的。

- (BOOL)setProperty:(id)property forKey:(NSStreamPropertyKey)key;

NSStreamFileCurrentOffsetKey
--Value is an NSNumber object containing the current absolute offset of the stream.

只要对NSInputStream指定它的NSStreamFileCurrentOffsetKey的值为你想要的offset ...

Category: iOS Tagged: 上传 断点续传 NSInputStream

comments

Read More

升级Xcode8后Jenkins打包问题

2016-09-29

升级Xcode8后Jenkins打包问题

上次说升级Xcode8之后,Jenkins自动打包就不行了,今天终于弄好了。前前后后话的时间有一天的时间才搞好,不容易。尝试了30多次才成功了,说多了都是泪。。现在记录一下。

我们的需求

说一下我们的需求。我们开发人员采用的是正式签名,正式的BundleID,但是我们Jenkins自动打包出来的是企业版,用的是企业版BundleID。代码是同一份,但是Jenkins打包企业版时时要先修改BundleID和相关版本号之类的。在Xcode7时代,我已经将Jenkins配置好,可以正常打出企业版的安装包。但是升级到Xcode8之后,Jenkins打包会报错,即原来的配置已经不能打出企业包来了。

解决方法

先说不能打包的原因,主要是签名方式冲突。Jenkins的配置是指定签名,而我们Xcode8采用的是自动签名。我们开发时采用自动签名,而且打企业版安装采用的签名文件跟我们开发时的签名文件时不一样的。所以Jenkins打包肯定得采用手动签名方式。

报错如下:

xxxxx has conflicting provisioning settings. xxxxx is automatically signed, but code signing identity xxxxxxxxxxxxx has been manually specified ...

Category: iOS Tagged: Jenkins Xcode8 iOS10 framework

comments

Read More

升级Xcode8后需要注意的事项

2016-09-24

升级Xcode8后需要注意的事项

随着iPhone7开卖适配iOS10成了必不可少的事情。要适配iOS10,升级Xcode8又是必须的事情。可以一旦升级Xcode8之后会发现一堆坑在里面。每次升级Xcode或者iOS系统或者Mac系统都是有一堆的坑,每次都要折腾得你死去活来不罢休。这有点像飞蛾扑火,明明知道前面是火坑,也要往下跳。

升级第一坑,注释代码快捷键失效

当你经过漫长的等待Xcode下载和安装之后,满心欢喜打开Xcode,发现字体变了,貌似看上去还不错(有同事跟我说这很丑),然后开始写代码,发现可以高亮显示当前编辑行,不错嘛,写着写着突然想注释掉一行代码,发现command+/怎么按都没反应,十分恼火。这什么破玩意儿,这么明显的bug都有。然后网上搜解决方案,确实有人也遇到这个问题。貌似需要跑一个命令什么的。实际上不用那么费劲,重启一下Xcode就好了。

升级第二坑,iOS的字体变宽了

当我Run一下程序想看看我们的程序在iOS10上有没有问题,启动之后打开引导页,发现最底下的两个按钮字显示不全了:注册账号 变成了 注···号,登录账号变成了 登···号,这是什么鬼?我没有动过这块的任何代码,甚至连这个页面的xib也没有打开过。后来才知道,运来是iOS的字体变宽了,如果你一个按钮在iOS9原来刚好放下4个字,然后iOS10它就装不下了 ...

Category: iOS Tagged: Jenkins Xcode8 iOS10

comments

Read More

iOS系统相册上传不得不说的那些事儿

2016-05-02

最近在开发手机相册自动备份的功能,这就需要用到上传文件的功能。 其实我一年前也做过同样的功能,当时也做得不算很好。这次是别人做了,然后那人离职了。我来接受这块,然后发现有问题,然后准备认真的研究一下这个问题,顺便学习更多的iOS开发的知识。

系统相册概述

首先说一下iOS相册的问题。iOS的相片都是存放在系统库ALAssetLibrary中,开发者从这个库中读取到的是一个个ALAsset对象,而不是一个文件系统的File文件。当然iOS8系统新增了Photo Framework,但是只是增强了相关功能,还是不能直接取到File。 这个Android平台或者PC平台很不一样的。

我们知道一般的上传文件流程就是,将本地的文件转换为二进制数据或流,通过HTTP协议或者socket协议,本地和服务器之间建立一个连接,本地将流写入这个连接,服务器那边接收这个流,并将接收到的流写入文件,直接客户端那边将整个文件传完,传输就结束了。上传文件这个过程一般会被封装库,使用者只需传入必要的参数就可以了。我以前做自动备份相册的时候,就是使用了别人提供的静态库,它上传文件API就是需要传一个文件的本地路径。这就麻烦了,因为我们的应用从iOS系统库读取到的对象是ALAsset, 所以我需要先将ALAsset从系统中导出,写入我们应用的沙盒中,然后再把它在沙盒中路径传给上传库进行上传。

导出ALAsset

据我所知,iOS系统cocoa层网络传输基本上只有NSURLConnection和基于NSURLSession一套API。它们提供的接口基本上都是基于NSData,或者文件路径或者NSInputStream。网上有很多库或者框架都是基于这两套API来做上传,我们一般也使用第三方的框架来减少工作量 ...

Category: iOS Tagged: iOS网络编程 系统相册上传 导出视频 NSURLConnection NSURLSession 大文件上传 后台上传

comments

Read More

swift初探

2014-06-22

swift初探

自从swift公布以来,火得不得了。如果你是开发者,还没有看过swift,那还真是out了(不过话说回来,并不是没有看过swift就不是好开发者。好的程序员跟看没看过某种语言没有什么关系)。我从公布以来就断断续续的看,一直在看官方的英文文档。偶尔也看看别人写的或者翻译的文章。现在虽然没有把官方文档看完,但是也看了大部分,可以写写感想心得。

简单来说,swift是集合了许多脚本语言的特性而又不像其他一般的脚本语言的苹果新生代开发语言。swift集合了很多语言的特性,如ruby,object-c,c++,c#等,其中长得最像的应该是ruby。咋看之下swift就想ruby一样的脚本语言。完全面向对象,类型定义,closure特性,函数式编程等等。不过据官方文档介绍,swift语言不是解释型语言,它最终会编译成二进制代码,并且它的运行效率比object-c要快。说比object-C要快我是比较怀疑的,而外国开发者进行了测试,得出结论是如果不强加一些编译优化的话,是快不过objC的。

如果对ruby,Object-C,c++语言熟悉的话,学起swift来应该没有什么难度。但是它还有很多语法特性是别的语言没有的,并且有些在我看来有点奇怪的特性。不过这是都没有什么,说明自己见识少,熟悉了就好了。首先一个比较特别的是,你不在需要在每行代码结束时加上分号,直接回车换行就行 ...

Category: iOS Tagged: swift

comments

Read More
Page 1 of 1