首页 > 解决方案 > 为什么后台 NSURLConnection 进入后台时出错?

问题描述

当我的应用程序进入后台时出现此错误。

NSURLConnection finished with error - code -1001 Task <09B84034-9F73-4DB6-A685-D891B1B1068A>.<2> finished with error - code: -1001

我正在使用此代码

- (id<XCDYouTubeOperation>) getVideoWithIdentifier:(NSString *)videoIdentifier completionHandler:(void (^)(XCDYouTubeVideo * __nullable video, NSError * __nullable error))completionHandler
{


    NSLog(@"Getting Video Identfifier");
    if (!completionHandler)
        @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"The `completionHandler` argument must not be nil." userInfo:nil];

    XCDYouTubeVideoOperation *operation = [[XCDYouTubeVideoOperation alloc] initWithVideoIdentifier:videoIdentifier languageIdentifier:self.languageIdentifier];
    operation.completionBlock = ^{
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
            if (operation.video || operation.error)
            {
                NSAssert(!(operation.video && operation.error), @"One of `video` or `error` must be nil.");
                completionHandler(operation.video, operation.error);
            }
            else
            {
                NSAssert(operation.isCancelled, @"Both `video` and `error` can not be nil if the operation was not canceled.");
            }
            operation.completionBlock = nil;
#pragma clang diagnostic pop
        }];
    };
    NSLog(@"Operation  - %@", operation ) ;
    [self.queue addOperation:operation];
    return operation;
}`





- (void) startRequestWithURL:(NSURL *)url type:(XCDYouTubeRequestType)requestType
{
    if (self.isCancelled)
        return;

    // Max (age-restricted VEVO) = 2×GetVideoInfo + 1×WatchPage + 1×EmbedPage + 1×JavaScriptPlayer + 1×GetVideoInfo
    if (++self.requestCount > 6)
    {
        // This condition should never happen but the request flow is quite complex so better abort here than go into an infinite loop of requests
        [self finishWithError];
        return;
    }

    XCDYouTubeLogDebug(@"Starting request: %@", url);

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
    [request setValue:self.languageIdentifier forHTTPHeaderField:@"Accept-Language"];

    NSLog(@"Request Type - ",requestType);

//    NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request];
//    [task resume];

    self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
    {
        if (self.isCancelled)
            return;

        if (error)
            [self handleConnectionError:error];
        else
            [self handleConnectionSuccessWithData:data response:response requestType:requestType];
    }];
    [self.dataTask resume];

    self.requestType = requestType;
}

#pragma mark - Response Dispatch

- (void) handleConnectionSuccessWithData:(NSData *)data response:(NSURLResponse *)response requestType:(XCDYouTubeRequestType)requestType
{

    NSLog(@"XCDDRequestType - ",requestType);

    CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)response.textEncodingName ?: CFSTR(""));
    // Use kCFStringEncodingMacRoman as fallback because it defines characters for every byte value and is ASCII compatible. See https://mikeash.com/pyblog/friday-qa-2010-02-19-character-encodings.html
    NSString *responseString = CFBridgingRelease(CFStringCreateWithBytes(kCFAllocatorDefault, data.bytes, (CFIndex)data.length, encoding != kCFStringEncodingInvalidId ? encoding : kCFStringEncodingMacRoman, false)) ?: @"";
    NSAssert(responseString.length > 0, @"Failed to decode response from %@ (response.textEncodingName = %@, data.length = %@)", response.URL, response.textEncodingName, @(data.length));

    XCDYouTubeLogVerbose(@"Response: %@\n%@", response, responseString);

    switch (requestType)
    {
        case XCDYouTubeRequestTypeGetVideoInfo:
            [self handleVideoInfoResponseWithInfo:XCDDictionaryWithQueryString(responseString) response:response];
            break;
        case XCDYouTubeRequestTypeWatchPage:
            [self handleWebPageWithHTMLString:responseString];
            break;
        case XCDYouTubeRequestTypeEmbedPage:
            [self handleEmbedWebPageWithHTMLString:responseString];
            break;
        case XCDYouTubeRequestTypeJavaScriptPlayer:
            [self handleJavaScriptPlayerWithScript:responseString];
            break;
    }
}

此代码将在后台自动运行,但几分钟后它会停止并给我上述错误。如何解决这个问题?

编辑 1 (Vinay Kiran 方法) #

我将 nsurlsession 配置更改为后台。

- (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(NSString *)languageIdentifier
{
    if (!(self = [super init]))
        return nil; // LCOV_EXCL_LINE

    _videoIdentifier = videoIdentifier ?: @"";
    _languageIdentifier = languageIdentifier ?: @"en";

//    _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"YouTubeID"];

    _session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];

    _operationStartSemaphore = dispatch_semaphore_create(0);

    NSLog(@"Initialize the Video Identifier");

    return self;
}

然后从后台更改完成处理程序,如果我使用处理程序,它将给出此错误

Swift:'后台会话不支持完成处理程序块。改用委托人。

    - (void) startRequestWithURL:(NSURL *)url type:(XCDYouTubeRequestType)requestType
{
    if (self.isCancelled)
        return;

    // Max (age-restricted VEVO) = 2×GetVideoInfo + 1×WatchPage + 1×EmbedPage + 1×JavaScriptPlayer + 1×GetVideoInfo
    if (++self.requestCount > 6)
    {
        // This condition should never happen but the request flow is quite complex so better abort here than go into an infinite loop of requests
        [self finishWithError];
        return;
    }

    XCDYouTubeLogDebug(@"Starting request: %@", url);

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
    [request setValue:self.languageIdentifier forHTTPHeaderField:@"Accept-Language"];

    NSLog(@"Request Type - ",requestType);

    // NEWLY ADDED
    NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request];
    [task resume];

//    self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
//    {
//        if (self.isCancelled)
//            return;
//
//        if (error)
//            [self handleConnectionError:error];
//        else
//            [self handleConnectionSuccessWithData:data response:response requestType:requestType];
//    }];
//    [self.dataTask resume];

    self.requestType = requestType;
}

现在的问题是我最初使用这个

    self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
    if (self.isCancelled)
        return;

    if (error)
        [self handleConnectionError:error];
    else
        [self handleConnectionSuccessWithData:data response:response requestType:requestType];
}];
[self.dataTask resume];

其中handleConnectionSuccessWithData 将接收数据、响应和请求类型。现在,如果我使用 backgroundSessionConfigurationWithIdentifier,我不知道在哪里可以获得数据、响应和请求类型。

标签: iosobjective-cbackgroundnsurlsessionnsurl

解决方案


使用后台线程代替主队列 backgroundSessionConfigurationWithIdentifier:

供参考 https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1407496-backgroundsessionconfigurationwi?language=objc


推荐阅读