在上一篇文章中,我写到如何为 NSScreencast iOS app 设计一个健壮的下载系统。
我们不应该强迫用户在前台完成下载,而是让 App 支持后台下载。
表面上看起来似乎很简单:使用后台会话配置进行配置后台会话,设置一个标识符,然后会进入一个单独的进程开始下载。
当使用后台会话,你就不能使用基于 block 的任务接口,因为会话和代理稍候可能需要被重新创建,以接收下载的更新。这其中有许多问题需要考虑,但我们先研究下乐观情况:
-
用户启动一个下载,然后挂起 app;
-
在一段时间后(以我经验是 10-30)app 会被终止运行;
-
下载会在单独的进程中继续进行;
-
当下载结束,app 会重新运行,app 的代理会收到 application(handleEventsForBackgroundSessionWithIdentifier:) 的回调,其中的标识符就是之前用于开始下载的标识符。
提示:当使用 Xcode 调试,当 app 在后台时,调试器实际上会阻止其被系统终止运行,所以我的策略是打开“Wait for Launch”选项(Edit Scheme>Run>Info>Wait for executable to be launched),然后手动启动 app 和下载,在启动调试器前,退出 app 到后台。
当调用该方法时,你的工作是用相同的标识符创建会话配置,创建会话,并设置代理。系统应该会立即通知代理其下载状态。
但是,哪种下载才是我们要的?
你确实不知道。你所能得到的只是原始请求的 URL,对你来说不一定足够。URL 不能作为关键的依据,因为它会变化,并且它可能不是唯一的。你有 http 和 https,多个路径可能指向同一个资源,又可能是重定向,等等。这里只列举了部分情况,总的来说,URL 并不会对你有很大的帮助。在我的情况中,我有相对规范的章节 URL,但它们都重定向到带签名的 Amazon CloudFront URLs,所以它们不是唯一的,同时是临时的。所以,我们要获取被通知的那个章节模型很困难。
奇怪的是这部分没有清楚写在文档中的 API,但我发现最好的策略是为每个下载创建一个唯一的会话标识符,并保存到模型中。这样,你就可以容易找到通知的那个下载。
好,乐观情况已成定局。但当下载出错时怎么处理?如果下载失败会发生什么?如果禁用蜂窝网络且在后台下载,当它们走出 Wi-Fi 覆盖范围会发生什么?
最后一种情况我能回答。如果你用普通会话配置配置会话,并在 Wi-Fi 环境开始下载,然后关掉,你会立即收到蜂窝网络下载未开启错误。但是,如果你使用后台会话,系统很智能地在 Wi-Fi 环境等待并稍候重试该请求。
这种情况同样会发生其他错误,包括实际不运行的本地服务器连接错误,导致下载显示正在运行,但卡在 0% 不动。启动本地服务器后,下载就开始,似乎什么错都没有。
文档中没有说明该请求将会等待多长时间重试。实际上,如果用户再次启动 app,下载是处于哪种状态?我们怎么获知?在开发中,在单独下载时我遇到一些情况。下载信息状态为 .downloading,但我们从未接收到完成、成功或其他回调。唯一办法就是将其标记为失败了,但主要问题是等待多久才这样做。因为下载是需要时间并且允许多次重试的,这就不是那么简单地在 X 分钟后标记它们为失败了。
这是我将可能做的,但感觉不太好。