美文网首页
取消、暂停下载任务和错误处理

取消、暂停下载任务和错误处理

作者: 醉看红尘这场梦 | 来源:发表于2020-03-14 10:39 被阅读0次

首先,我们来处理下载结束事件。之所以说是下载结束,而不是下载完成,是因为如果下载过程中发生了错误,那么整个下载过程也就结束了。接下来,我们就从处理下载错误开始。


处理下载结束事件

处理下载错误

继续在beginDownload里,添加下面的代码:


if let resUrl = self.downloadUrl.text {
  Alamofire.download(.GET, resUrl, destination: dest)
    .progress { 
      bytesRead, 
      totalBytesRead, 
      totalBytesExpectedToRead in
        // This closure is NOT called on the 
        // main queue for performance reasons. 
        // To update your ui, dispatch to the main queue.
        dispatch_async(dispatch_get_main_queue()) {
          let progress = 
          Float(totalBytesRead) / Float(totalBytesExpectedToRead)
          self.downloadProgress.progress = progress
        }
    }
    .response { request, response, data, error in
       if let error = error {
         print("Download error: \(error.localizedDescription)")
       }
       else {
         // Download successfully
       }
    }
}

这个response方法,我们在前面的视频中提到过,我们可以在progress后面串联起来处理下载结束的事件。当error不为nil时,我们就打印错误的详细信息。

按Command + R重新编译执行,重新下载我们之前的文件,在下载的过程中,我们关掉WiFi,就可以在控制台看到下载错误的提示了:

image

在发生下载异常之后,我们能做的事情当然不仅仅是向控制台打印个消息这么简单,Alamofire还提供了恢复下载的功能,我们可以继续完善下载错误的处理机制。


Resume data

在response的参数里,有一个data,它是一个NSData optional对象,当有可恢复下载的内容时,它不为nil,我们可以利用它来恢复下载。

继续在.response里,添加下面的代码:


.response { [unowned self] (request, response, data, error) in
  if let error = error {
    print("Download error: \(error.localizedDescription)")

      if let data = data {
        print("Resume data exists")
        self.displayNetworkAlert(
          error.localizedDescription, 
          data: data, 
          destination: dest)
      }
      else {
        print("Resume data is nil")
        self.displayNetworkAlert(
        error.localizedDescription)
      }
  }
  else {
    // Download successfully
  }
}

这里,我们假设有一个叫做displayNetworkAlert的方法,它可以在发生网络错误的时候,给用户弹一个Alert。当存在可恢复数据时,允许用户恢复下载,否则,只是简单提示用户错误。接下来,我们来实现它。

我们把displayNetworkAlert的代码,添加到一个ViewController extension里:


extension ViewController {
  private func displayNetworkAlert(errorMessage: String,
    data: NSData? = nil,
    destination: Request.DownloadFileDestination? = nil) {
      let alert = UIAlertController(
        title: "Network error",
        message: errorMessage,
        preferredStyle: .Alert)

      if let data = data {
        alert.addAction(UIAlertAction(title: "Resume",
            style: UIAlertActionStyle.Default,
            handler: { _ in
              // Resume download here
              print("Resume downloading...")
        }))

        alert.addAction(UIAlertAction(title: "Cancel",
            style: UIAlertActionStyle.Cancel,
            handler: { _ in
              // Reset the app
              print("Cancel downloading...")
        }))
      }
      else {
        alert.addAction(UIAlertAction(title: "OK",
          style: UIAlertActionStyle.Default,
          handler: { _ in
            // Cancel download here
            print("Cancel downloading...")
          }))
      }

      self.presentViewController(alert, 
        animated: true, completion: nil)
    }
}

这段代码逻辑很简单,我们显示了用一个<key style="box-sizing: border-box;">UIAlertController</key>显示了错误信息,并且根据是否可以恢复下载给了用户不同的选项。然后,我们来实现不同的action,首先是恢复下载:


alert.addAction(UIAlertAction(title: "Resume",
  style: UIAlertActionStyle.Default,
    handler: { _ in
      // Resume download here
      print("Resume downloading...")

      if let destination = destination {
        defer {
          Alamofire.download(resumeData: data, 
            destination: destination)
            .progress { 
              bytesRead, 
              totalBytesRead, 
              totalBytesExpectedToRead in
                dispatch_async(dispatch_get_main_queue()) {
                  let progress = 
                    Float(totalBytesRead) / Float(totalBytesExpectedToRead)
                  self.downloadProgress.progress = progress
                }
            }
        }
      }
}))

简单起见,我们只列出了恢复下载的部分。Alamofire.download有一个重载的版本,它接受一个NSData和一个DownloadFileDestination,并且尝试重新恢复下载。然后我们用和之前类似的方式继续更新UIProgressView。

另外一个值的说明的地方是defer关键字。这是因为,当我们点击Resume按钮之后,当<key style="box-sizing: border-box;">UIAlertController</key>还在撤销自己时,如果我们执行到了progress中的代码,就会在控制台看到一些奇怪的错误提示。为了避免这个错误,我们要保证progress中的代码至少执行完一次之后,整个<key style="box-sizing: border-box;">UIAlertController</key>才彻底消失。因此,我们把恢复下载的代码用defer包围了起来。

这时,按Command + R重现编译执行,我们的App就可以恢复下载了。

image

然后我们来处理用户点击Cancel以及不可恢复下载的情况,这种情况很简单,我们只要重置App的UI就可以了:


alert.addAction(UIAlertAction(title: "Cancel",
    style: UIAlertActionStyle.Cancel,
    handler: { _ in
        // Reset the app
        print("Cancel downloading...")

        self.downloadUrl.text = nil
        self.downloadProgress.progress = 0
        self.beginBtn.enabled = false
        self.suspendOrResumeBtn.enabled = false
        self.cancelBtn.enabled = false
}))

最后,我们添加下载成功完成的提示:


.response { request, response, data, error in
    if let error = error {
        // Omit for simplicity...
    }
    else {
        // Download successfully
        let alert = UIAlertController(title: "Success",
            message: "Download finished successfully!",
            preferredStyle: .Alert)

        alert.addAction(UIAlertAction(title: "OK",
            style: UIAlertActionStyle.Default,
            handler: { _ in
                print("Finish downloading...")

                self.downloadUrl.text = nil
                self.downloadProgress.progress = 0
                self.beginBtn.enabled = false
                self.suspendOrResumeBtn.enabled = false
                self.cancelBtn.enabled = false
        }))

        self.presentViewController(alert, 
            animated: true, completion: nil)
    }
}

image

这样,下载这部分的功能就开发完了。


暂停和恢复下载

暂停和恢复下载的功能实现起来非常简单,之前的Alamofire.download方法,会返回一个Alamofire.Request对象,我们调用它的suspend和resume方法就可以了。首先,在ViewController里,我们添加一个属性:


class ViewController: UIViewController {
    var currStatus = DownloadStatus.NotStarted
    var request: Alamofire.Request?

    // Omit for simplicity...
}

然后,在发起下载请求的时候,保存这个Request:


// Button actions
@IBAction func beginDownload(sender: AnyObject) {
    print("Begin downloading...")

    /*
     * Omit for simplicity...
     */

    if let resUrl = self.downloadUrl.text {
        self.request = Alamofire.download(.GET, 
            resUrl, destination: dest)
    }

    /*
     * Omit for simplicity...
     */
}

最后,在suspendOrResumeDownload方法里,添加下面的代码:


// Button actions
@IBAction func suspendOrResumeDownload(sender: AnyObject) {
    var btnTitle: String?

    switch self.currStatus {
    case .Downloading:
        print("Suspend downloading...")

        // TODO: Add suspending code here
        self.currStatus = .Suspended
        btnTitle = "Resume"

        self.request!.suspend()
    case .Suspended:
        print("Resume downloading...")

        // TODO: Add resuming code here
        self.currStatus = .Downloading
        btnTitle = "Suspend"

        self.request!.resume()
    case .NotStarted, .Cancelled:
        break
    }

    self.suspendOrResumeBtn.setTitle(btnTitle, 
        forState: UIControlState.Normal)
}

很简单,我们只是调用了对应的Alamofire.Request对象的suspend()和resume()方法。然后,按Command + R重新执行,我们就可以看到对应的结果了。

image

取消下载

终于到最后一个功能了。一个好消息就是,取消下载和暂停/恢复下载一样的简单,调用Request对象的cancel()方法就好。我们给Cancel按钮的事件处理函数添加下面的代码:


// Button actions
@IBAction func cancelDownload(sender: AnyObject) {
    print("Cancel downloading...")

    switch self.currStatus {
    case .Downloading, .Suspended:
        // TODO: Add cancel code here

        self.request?.cancel()

        self.currStatus = .Cancelled
        self.cancelBtn.enabled = false
        self.suspendOrResumeBtn.enabled = false
        self.suspendOrResumeBtn.setTitle("Suspend", 
            forState: UIControlState.Normal)
    default:
        break
    }
}

这里,有一点要说明的是,如果你为特定的request"注册"了错误处理函数(就像我们之前在.response()中那样),那么调用cancel()方法将触发对应的错误处理函数,像这样:

image

此时,如果你点击Resume按钮,下载仍然是可以恢复进行的。


相关文章

网友评论

      本文标题:取消、暂停下载任务和错误处理

      本文链接:https://www.haomeiwen.com/subject/prtyjhtx.html