如何处理后台URL会话丢失互联网连接



>我在后台下载zip文件:

if let url = NSURL(string: urlstring) 
         {            
            let config = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier((NSUUID().UUIDString))
            let session = NSURLSession(configuration: config, delegate: self, delegateQueue: nil)
            let task = session.downloadTaskWithURL(url)
            session.sessionDescription = filepath
            if let sessionId = session.configuration.identifier
            {
                print("start zip session: " + sessionId)
            }
            task.resume()               
            }
        }

如果您有互联网连接,它可以很酷,但如果您在下载应用程序期间丢失了它,只需等待和URLSession(会话:NSURLSession,任务:NSURLSessionTask,didCompleteWithError error:NSError?(将不会被称为永远不会。如何处理?
类似于服务器响应的时间

这个问题很老,但仍然没有答案,所以这里有一个解决方法。

先澄清一些

通常,URLSessionConfiguration有一个称为waitsForConnectivity的属性,可以将其设置为false,然后URLSessionTask在连接丢失时将直接失败。但是,如果true,则URLSessionTaskDelegate将收到urlSession(_:taskIsWaitingForConnectivity:)方法的回调。

然而

后台任务(如DownloadTask(始终等待连接并忽略waitsForConnectivity URLSessionConfiguration 的属性。它们也不会触发urlSession(_:taskIsWaitingForConnectivity:)回调,因此没有官方方法来侦听下载任务上的连接中断。

解决方法

如果您侦听下载进度,您会注意到对该方法的调用在一秒钟内完成几次。因此,我们可以得出结论,如果调用进度回调的时间超过 5 秒,则可能存在连接丢弃问题。因此,解决方法是向URLSessionDownloadDelegate委托创建其他属性并存储进度的上次更新。然后具有间隔功能,定期检查此属性是否未很快更新。

像这样:

    class Downloader: NSObject, URLSessionTaskDelegate, URLSessionDownloadDelegate {
        var lastUpdate: Date;
        var downloadTask: URLSessionDownloadTask?;
        public var session : URLSession {
            get {
                let config = URLSessionConfiguration.background(
                withIdentifier: "(Bundle.main.bundleIdentifier!).downloader");
                config.isDiscretionary = true;
                return URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue());
            }
        }
        override init() {
            self.lastUpdate = Date();
            super.init();
        }
        func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
            // Handle download completition
            // ...
        }
        func urlSession(
            _ session: URLSession,
            downloadTask: URLSessionDownloadTask,
            didWriteData bytesWritten: Int64,
            totalBytesWritten writ: Int64,
            totalBytesExpectedToWrite exp: Int64)
        {
            let progress = 100 * writ / exp;
            // Do something with the progress
            // ...
            self.lastUpdate = Date();
        }
    }
    var downloader = Downloader();
    let url = "http://...";
    var request = URLRequest(url: URL(string: url)!);
    currentWorker.downloadTask = downloader.session.downloadTask(with: request);
    currentWorker.downloadTask!.resume();
    // Schedule timer for every 5 secs
    var timer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "CheckInternetDrop", userInfo: nil, repeats: true);
func CheckInternetDrop(){
    let interval = Date().timeIntervalSinceDate(downloader.lastUpdate);
    if (interval > 5) {
        print("connection dropped");
    }
}

非常简单的例子...

根据苹果关于暂停和恢复下载的文档:

此外,使用后台配置的下载将自动处理恢复,因此只有非后台下载才需要手动恢复。

此外,等待连接被忽略的原因在后台下载文件中说明:

如果您的应用在后台运行,则在另一个进程中执行下载时,系统可能会挂起您的应用。

这就是为什么即使您以类似于Dimitar Atanasov的方式构建计时器,一旦应用程序进入后台,它也不会工作,因为它将被挂起。

我已经测试了后台下载自己与网络故障,系统恢复没有失败,因此不需要额外的代码。

您可以为请求设置超时:

config.timeoutIntervalForRequest = <desired value>
config.timeoutIntervalForResource = <desired value>

文档:

  • timeoutIntervalForRequest
  • timeoutIntervalForResource

最新更新