使用后台配置时,NSURLSessionTask在超时后永远不会回调

时间:2014-04-25 09:08:15

标签: ios ios7.1 nsurlsession nsurlsessiondownloadtask nsurlsessionconfiguration

我正在使用NSURLSessionDownloadTask后台会话来实现我的所有REST请求。这样我可以使用相同的代码,而不必考虑我的应用程序在后台或前台。

我的后端已经死了一段时间,我借此机会测试NSURLSession如何表现超时。

令我惊讶的是,我的NSURLSessionTaskDelegate回调都没有被召唤过。无论我在NSURLRequestNSURLSessionConfiguration上设置了什么超时,我都不会从iOS收到任何回调,告诉我请求确实已经超时完成。

也就是说,当我在后台会话上启动NSURLSessionDownloadTask时。应用程序在后台或前台发生相同的行为。

示例代码:

- (void)launchDownloadTaskOnBackgroundSession {
    NSString *sessionIdentifier = @"com.mydomain.myapp.mySessionIdentifier";
    NSURLSessionConfiguration *backgroundSessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:sessionIdentifier];
    backgroundSessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringCacheData;
    backgroundSessionConfiguration.timeoutIntervalForRequest = 40;
    backgroundSessionConfiguration.timeoutIntervalForResource = 65;
    NSURLSession *backgroundSession = [NSURLSession sessionWithConfiguration:backgroundSessionConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]];

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.timeout.com/"]];
    request.timeoutInterval = 30;
    NSURLSessionDownloadTask *task = [backgroundSession downloadTaskWithRequest:request];
    [task resume];
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    NSLog(@"URLSession:task:didCompleteWithError: id=%d, error=%@", task.taskIdentifier, error);
}

但是,当我使用默认会话时,我会在30秒后收到错误回调(我在请求级别设置的超时)。

示例代码:

- (void)launchDownloadTaskOnDefaultSession {
    NSURLSessionConfiguration *defaultSessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
    defaultSessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringCacheData;
    defaultSessionConfiguration.timeoutIntervalForRequest = 40;
    defaultSessionConfiguration.timeoutIntervalForResource = 65;
    NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultSessionConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]];

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.timeout.com/"]];
    request.timeoutInterval = 30;
    NSURLSessionDownloadTask *task = [defaultSession downloadTaskWithRequest:request];
    [task resume];
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    NSLog(@"URLSession:task:didCompleteWithError: id=%d, error=%@", task.taskIdentifier, error);
}

我似乎无法在文档中找到任何暗示在使用后台会话时超时应该表现不同的内容。

是否还有人遇到过这个问题? 这是一个错误还是一个功能?

我正在考虑创建一个错误报告,但我通常会在SO(几分钟)上比在bug报告者(六个月)上获得更快的反馈。

此致

4 个答案:

答案 0 :(得分:20)

从iOS8开始,如果服务器没有响应,则后台模式下的NSUrlSession不会调用此委托方法。 -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
下载/上传无限期保持空闲状态。 当服务器没有响应时,在iOS7上调用此委托时出错。

  

通常,NSURLSession后台会话不会使任务失败   电线上出了问题。相反,它继续寻找一个   是时候运行请求并重试了。这继续下去   直到资源超时到期(即,值的   NSURLSessionConfiguration中的timeoutIntervalForResource属性   用于创建会话的对象)。当前的默认值   价值是一周!

Quoted information taken from this Source

换句话说,iOS7中超时失败的行为是不正确的。在后台会话的上下文中,由于网络问题而不立即失败更有意思。因此,自iOS8以来,NSURLSession任务即使遇到超时和网络丢失也会继续。但是它会一直持续到达timeoutIntervalForResource。

所以基本上timeoutIntervalForRequest在后台会话中不起作用,但是timeoutIntervalForResource会。

答案 1 :(得分:4)

NSURLSessionTaskDelegate抛出DownloadTask超时而不是NSURLSessionDownloadDelegate

在downloadTask期间触发超时(-1001):

等到下载开始。 数据下载的百分比块将触发:

<强> URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:

然后在XCode调试器中暂停整个应用程序。

等待30秒。

使用XCode调试器按钮取消暂停应用

来自服务器的http连接应该超时并触发:

-1001&#34;请求超时。&#34;

#pragma mark -
#pragma mark NSURLSessionTaskDelegate - timeouts caught here not in DownloadTask delegates
#pragma mark -
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{

    if(error){

        ErrorLog(@"ERROR: [%s] error:%@", __PRETTY_FUNCTION__,error);

        //-----------------------------------------------------------------------------------
        //-1001 "The request timed out." 
//        ERROR: [-[SNWebServicesManager URLSession:task:didCompleteWithError:]] error:Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSUnderlyingError=0x1247c42e0 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, NSErrorFailingURLStringKey=https://directory.clarksons.com/api/1/dataexport/ios/?lastUpdatedDate=01012014000000, NSErrorFailingURLKey=https://directory.clarksons.com/api/1/dataexport/ios/?lastUpdatedDate=01012014000000, _kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-2102, NSLocalizedDescription=The request timed out.}
        //-----------------------------------------------------------------------------------


    }else{
        NSLog(@"%s SESSION ENDED NO ERROR - other delegate methods should also be called so they will reset flags etc", __PRETTY_FUNCTION__);
    }
}

答案 2 :(得分:3)

UIApplicationDelegate中有一种方法可以让您了解后台进程。

-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler

如果有多个会话,您可以通过

识别您的会话
if ([identifier isEqualToString:@"com.mydomain.myapp.mySessionIdentifier"]) 

还有一种方法用于定期通知进度。在这里你可以查看NSURLSession的状态

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite


NSURLSessionTaskStateRunning = 0,                   
NSURLSessionTaskStateSuspended = 1,
NSURLSessionTaskStateCanceling = 2,                   
NSURLSessionTaskStateCompleted = 3,              

答案 3 :(得分:3)

与您一样,我正在处理的应用始终使用后台会话。我注意到的一件事是,如果超时正在中断工作连接,即传输成功启动,则超时工作正常。但是,如果我为不存在的URL启动下载任务,则不会超时。

鉴于你说你的后端已经死了一段时间,这听起来很像你所看到的。

很容易重现。只需设置超时5秒。使用有效的URL,您将获得一些进度更新,然后看到它超时。即使有后台会话。使用无效的网址,只要您致电简历,它就会变得安静。