如何在下次启动时恢复AVAggregateAssetDownloadTask下载任务?
解决AVAggregateAssetDownloadTask重启APP后恢复下载的方案
我来帮你搞定这个问题~虽然旧的指定目标文件夹的API被废弃了,但结合URLSession的后台恢复机制+本地元数据持久化,完全可以实现重启后恢复下载的需求,具体步骤如下:
一、核心思路
现在的AVAggregateAssetDownloadTask基于AVAssetDownloadURLSession(继承自URLSession),所以我们要利用URLSession的后台任务恢复能力,再配合本地保存的下载元数据,就能在APP重启后找回未完成的任务,或者重新构建任务继续下载。
二、具体实现步骤
1. 下载前持久化关键元数据
在启动下载前,把恢复任务必需的信息保存到本地(比如UserDefaults或CoreData),至少要包含:
- 待下载资源的
AVURLAsset的URL - 下载时指定的
AVMediaSelection(如果有音轨/字幕选择的话) - 自定义的业务标识(比如视频ID,方便关联业务逻辑)
示例代码:
// 假设你已经创建了AVURLAsset和对应的mediaSelection let assetURL = URL(string: "你的流媒体地址")! let mediaSelection = asset.preferredMediaSelection // 把信息转成可持久化的格式 let downloadMetaData = [ "assetURL": assetURL.absoluteString, "mediaSelectionData": mediaSelection.dataRepresentation(), "videoID": "自定义业务ID" ] as [String: Any] // 保存到UserDefaults UserDefaults.standard.set(downloadMetaData, forKey: "PendingAssetDownload")
2. 启动APP时恢复任务
APP启动后,先通过固定标识的后台URLSession创建实例,系统会自动帮我们找回未完成的任务;如果系统没恢复成功,再用本地保存的元数据重新构建任务。
示例代码:
// 注意:这里的session identifier必须固定,系统靠它识别后台任务 let config = URLSessionConfiguration.background(withIdentifier: "com.your.app.assetDownloadSession") let downloadSession = AVAssetDownloadURLSession(configuration: config, assetDownloadDelegate: self, delegateQueue: OperationQueue.main) // 第一步:获取系统已恢复的任务 downloadSession.getAllTasks { tasks in for task in tasks { if let aggregateTask = task as? AVAggregateAssetDownloadTask { // 关联你的业务逻辑,比如更新UI显示下载进度 self.continueExistingTask(aggregateTask) } } } // 第二步:如果没有找到可恢复的任务,用本地元数据重新创建 if let metaData = UserDefaults.standard.dictionary(forKey: "PendingAssetDownload"), let assetURLStr = metaData["assetURL"] as? String, let assetURL = URL(string: assetURLStr), let mediaData = metaData["mediaSelectionData"] as? Data { let asset = AVURLAsset(url: assetURL) guard let mediaSelection = try? AVMediaSelection(dataRepresentation: mediaData, for: asset) else { return } // 创建新的聚合下载任务并启动 let newTask = downloadSession.aggregateAssetDownloadTask(with: asset, mediaSelections: [mediaSelection], assetDownloadOptions: nil) newTask?.resume() }
3. 处理代理方法,维护任务状态
在AVAssetDownloadDelegate的回调里,更新下载进度、处理完成/错误情况,同时及时更新本地持久化的信息:
// 单个媒体轨道下载完成 func urlSession(_ session: URLSession, aggregateAssetDownloadTask: AVAggregateAssetDownloadTask, didCompleteFor mediaSelection: AVMediaSelection) { // 可以在这里更新单个轨道的完成状态 } // 下载进度更新 func urlSession(_ session: URLSession, aggregateAssetDownloadTask: AVAggregateAssetDownloadTask, didLoad timeRange: CMTimeRange, totalTimeRangesLoaded loadedTimeRanges: [NSValue], timeRangeExpectedToLoad: CMTimeRange) { // 计算进度并更新UI let totalLoaded = loadedTimeRanges.reduce(CMTime.zero) { $0 + $1.timeRangeValue.duration } let progress = CMTimeGetSeconds(totalLoaded) / CMTimeGetSeconds(timeRangeExpectedToLoad.duration) print("当前下载进度:\(progress)") } // 任务完成/出错 func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { if let error = error { let nsError = error as NSError // 如果是用户主动取消,清除本地持久化数据 if nsError.code == NSURLErrorCancelled { UserDefaults.standard.removeObject(forKey: "PendingAssetDownload") } // 其他错误(比如网络中断),保留本地数据,下次启动继续 } else { // 下载完全完成,清除本地持久化数据 UserDefaults.standard.removeObject(forKey: "PendingAssetDownload") } }
三、关键注意事项
- 必须使用后台URLSession配置(
background(withIdentifier:)),并且identifier要固定,这是系统恢复后台任务的核心依据。 - 不要手动指定下载目标路径,现在的API由系统统一管理离线内容的存储位置,你可以通过
aggregateAssetDownloadTask.urlAsset直接访问已下载的离线资源。 - 持久化的元数据要足够完整,尤其是
AVMediaSelection,因为AVAggregateAssetDownloadTask是针对特定媒体选择(比如音轨、字幕)创建的,缺失的话无法正确恢复任务。
内容的提问来源于stack exchange,提问作者user1203625
相关产品推荐
相关产品推荐

