乐于分享
好东西不私藏

【开发者】iOS下载器中两个令人窒息的诡异Bug及破局思路

【开发者】iOS下载器中两个令人窒息的诡异Bug及破局思路

本篇文章不会聚焦iOS下载器的具体实现,主要是剖析和解决两个棘手的下载异常。

一是在下载的过程中,把App退到后台待一会再进入前台,大概率会触发下载失败。

二是下载过程中反复退后台进前台再杀死App,重新打开App,大概率会碰到下载失败并且反复重试总是失败。

下载器整体逻辑

我们的下载器底层使用的是Alamofire,并且支持断点续传。

问题一  退后台后下载失败

我们的App并没有申请Background mode,因此推测是跟App退到后台下载线程被挂起有关,而且这个问题在release包尤其明显。

解决方案

申请后台任务时间,防止切后台被挂起导致连接中断。

if backgroundTaskID == .invalid {      backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "Downloader-\(id)") { [weak selfin           self?.endBackgroundTask()       }  }  func endBackgroundTask() {        if backgroundTaskID != .invalid {            UIApplication.shared.endBackgroundTask(backgroundTaskID)            backgroundTaskID = .invalid        }    }

对于下载失败,拦截-1005(连接断开)错误并尝试静默续传重试。同时设置静默续传的次数,防止无限次重试。

case NSURLErrorNetworkConnectionLost:      downloadError = EPODownloadError(code: .networkConnectionLost)       // 拦截 -1005 (Connection Lost) 错误并尝试静默续传重试       if let resumeData = response.resumeData, current1005RetryCount < max1005RetryCount {            self.saveResumeData(resumeData)             current1005RetryCount += 1             shouldSilentRetry = true         } elseif let resumeData = response.resumeData {              self.saveResumeData(resumeData)        }

问题二 下载重试总是失败

通过分析下载失败的日志,发现下载失败的原因总是“文件校验失败”。

因此怀疑是新的下载任务受到了之前“脏数据”的污染。

解决方案

在下载任务启动前,如果不需要断点续传,则强制清理目标路径下的残留“脏文件”。

if resumeModel == nil {if FileManager.default.fileExists(atPath: fileURL.path) {try? FileManager.default.removeItem(at: fileURL)    }  }

如果文件校验失败,除了强制清理上次下载的脏数据之外,同时清理下断点续传的文件数据。

clearResumeData() 

亲测通过上面的解决方案,这两个问题都得到了比较好的解决。

1

END

1

球分享

球点赞

球在看