【问题标题】:how can i track download progress after app is did become active from background应用程序从后台激活后如何跟踪下载进度
【发布时间】:2022-02-24 12:45:09
【问题描述】:

我有一个标题中描述的问题。您可能会在我的存储库 (https://github.com/Hudayberdyyev/custom_download_manager) 中看到源代码。我将尝试简要解释这个问题。我正在尝试基于此 repo (https://github.com/r-plus/HLSion) 编写下载管理器。基本上它由3部分组成:

  1. SessionManager(管理所有会话)
  2. HLSData(HLSData模型,初始化和下面代码一样,就像是会话管理器之间的中介)
public convenience init(url: URL, options: [String: Any]? = nil, name: String) {
        let urlAsset = AVURLAsset(url: url, options: options)
        self.init(asset: urlAsset, description: name)
}
  1. AssetStore(它是托管的 HLSData.plist 文件。其中包含每个下载会话的名称和路径)。

这是开始下载的实现方式:

var sources = [HLSData]()
@objc func startDownloadButtonTapped() {
        print(#function)
        let hlsData = sources[0]
        switch hlsData.state {
        case .notDownloaded:
            hlsData.download { (percent) in
                DispatchQueue.main.async {
                    print("percent = \(percent)")
                    self.percentLabel.text = "\(percent)"
                }
            }.finish { (relativePath) in
                DispatchQueue.main.async {
                    print("download completed relative path = \(relativePath)")
                }
            }.onError { (error) in
                print("Error finish. \(error)")
            }
        case .downloading:
            print("State is downloading")
            break
        case .downloaded:
            print(hlsData.localUrl ?? "localURL is nil")
        }
}

点击前的状态是notDownloaded。当点击按钮并将状态更改为 downloading 时,应用程序分别开始下载。 一切正常,进度跟踪良好。但是当我回到后台并返回应用程序时,状态仍然在下载,但进度关闭不再起作用。我如何恢复或重置此关闭以跟踪进度。提前致谢。

【问题讨论】:

    标签: ios swift xcode download


    【解决方案1】:

    在做一些测试时,我觉得iOS 12 and below 有一个错误,AVAssetDownloadDelegate

    在进行一些测试时,我在尝试使用 AVAssetDownloadTask 通过 HLS 下载媒体时注意到以下内容:

    iOS 13 及更高版本

    1. 进入后台时,继续下载
    2. 当从后台进入前台时,AVAssetDownloadDelegate 仍然会触发assetDownloadTask didLoad totalTimeRangesLoaded 并且可以更新进度
    3. 暂停或退出应用后,使用相同的URLSessionConfiguration identifier 重新初始化AVAssetDownloadURLSession,下载会自动从上次停止的地方继续

    iOS 12 及更低版本

    除了第 2 点之外,一切几乎都成立,由于某种原因,assetDownloadTask didLoad totalTimeRangesLoaded 从后台进入前台时不再被触发,因此进度不再更新。

    我从这个答案https://stackoverflow.com/a/55847387/1619193 得到的一个解决方法是,在过去,必须在应用程序因AVAssetDownloadTask 暂停后手动恢复下载,方法是为其提供磁盘上部分下载文件的位置。

    根据文档:

    AVAssetDownloadTask 提供恢复先前停止的能力 在某些情况下下载。为此,只需实例化一个 带有用文件实例化的 AVURLAsset 的新 AVAssetDownloadTask NSURL 指向部分下载的包含所需的捆绑包 下载选项,并且下载将继续恢复任何 以前下载的数据。

    有趣的是,您再也无法在官方文档中找到它,而且似乎设置 destinationURL 已被弃用,因此似乎对事情的工作方式进行了一些重构。

    我的解决方案:

    1. 订阅UIApplication.willEnterForegroundNotification 通知
    2. 在UIApplication.willEnterForegroundNotification的回调中,检查设备是否运行iOS 12及以下版本
    3. 如果是,取消当前的AVAssetDownloadTask
    4. 这应该会触发AVAssetDownloadDelegate 回调assetDownloadTask didFinishDownloadingTo,这将为您提供部分下载文件的位置
    5. 重新配置AVAssetDownloadTask,但不要使用 HLS url 配置它,而是使用部分下载资产的 URL 配置它
    6. 继续下载,AVAssetDownloadDelegate 的进度似​​乎又开始触发了

    你可以下载这个here的例子

    以下是上述步骤的一些小sn-ps:

    private let downloadButton = UIButton(type: .system)
    
    private let downloadTaskIdentifier = "com.mindhyve.HLSDOWNLOADER"
    
    private var backgroundConfiguration: URLSessionConfiguration?
    private var assetDownloadURLSession: AVAssetDownloadURLSession!
    private var downloadTask: AVAssetDownloadTask!
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
    
        // UI configuration left out intentionally
        subscribeToNotifications()
        initializeDownloadSession()
    }
    
    private func initializeDownloadSession()
    {
        // This will create a new configuration if the identifier does not exist
        // Otherwise, it will reuse the existing identifier which is how a download
        // task resumes
        backgroundConfiguration
            = URLSessionConfiguration.background(withIdentifier: downloadTaskIdentifier)
        
        // Resume will happen automatically when this configuration is made
        assetDownloadURLSession
            = AVAssetDownloadURLSession(configuration: backgroundConfiguration!,
                                        assetDownloadDelegate: self,
                                        delegateQueue: OperationQueue.main)
    }
    
    private func resumeDownloadTask()
    {
        var sourceURL = getHLSSourceURL(.large)
        
        // Now Check if we have any previous download tasks to resume
        if let destinationURL = destinationURL
        {
            sourceURL = destinationURL
        }
        
        if let sourceURL = sourceURL
        {
            let urlAsset = AVURLAsset(url: sourceURL)
            
            downloadTask = assetDownloadURLSession.makeAssetDownloadTask(asset: urlAsset,
                                                                         assetTitle: "Movie",
                                                                         assetArtworkData: nil,
                                                                         options: nil)
            
            downloadTask.resume()
        }
    }
    
    func cancelDownloadTask()
    {
        downloadTask.cancel()
    }
    
    private func getHLSSourceURL(_ size: HLSSampleSize) -> URL?
    {
        if size == .large
        {
            return URL(string: "https://video.film.belet.me/45505/480/ff27c84a-6a13-4429-b830-02385592698b.m3u8")
        }
        
        return URL(string: "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8")
    }
    
    // MARK: INTENTS
    @objc
    private func downloadButtonTapped()
    {
        print("\(downloadButton.titleLabel!.text!) tapped")
        
        if downloadTask != nil,
           downloadTask.state == .running
        {
            cancelDownloadTask()
        }
        else
        {
            resumeDownloadTask()
        }
    }
    
    @objc
    private func didEnterForeground()
    {
        if #available(iOS 13.0, *) { return }
        
        // In iOS 12 and below, there seems to be a bug with AVAssetDownloadDelegate.
        // It will not give you progress when coming from the background so we cancel
        // the task and resume it and you should see the progress in maybe 5-8 seconds
        if let downloadTask = downloadTask
        {
            downloadTask.cancel()
            initializeDownloadSession()
            resumeDownloadTask()
        }
    }
    
    private func subscribeToNotifications()
    {
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(didEnterForeground),
                                               name: UIApplication.willEnterForegroundNotification,
                                               object: nil)
    }
    
    // MARK: AVAssetDownloadDelegate
    func urlSession(_ session: URLSession,
                    task: URLSessionTask,
                    didCompleteWithError error: Error?)
    {
        guard error != nil else
        {
            // download complete, do what you want
            return
        }
        
        // something went wrong, handle errors
    }
    
    func urlSession(_ session: URLSession,
                    assetDownloadTask: AVAssetDownloadTask,
                    didFinishDownloadingTo location: URL)
    {
        // Save the download path of the task to resume downloads
        destinationURL = location
    }
    

    如果有些地方看起来不合适,我建议查看完整的工作示例here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-10
      • 2021-11-20
      • 2016-09-06
      • 1970-01-01
      • 1970-01-01
      • 2018-02-17
      • 2021-10-30
      • 2015-01-17
      相关资源
      最近更新 更多