奇怪的NSURLSessionDownloadTask行为通过蜂窝(不是wifi)

问题描述 投票:13回答:4

我已启用具有远程通知任务的后台模式,以便在应用程序收到推送通知时在后台下载小文件(100kb)。我已经使用配置了下载会话

NSURLSessionConfiguration *backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:sessionIdentifier];
[backgroundConfiguration setAllowsCellularAccess:YES];


self.backgroundSession = [NSURLSession sessionWithConfiguration:backgroundConfiguration
                                                       delegate:self
                                                  delegateQueue:[NSOperationQueue mainQueue]];

并使用激活它

 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[hostComponents URL]];

[request setAllowsCellularAccess:YES];


NSMutableData *bodyMutableData = [NSMutableData data];
[bodyMutableData appendData:[params dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:[bodyMutableData copy]];


_downloadTask =  [self.backgroundSession downloadTaskWithRequest:request];

[self.downloadTask resume];

现在一切正常,只有当我通过Wifi或通过Cellular连接但iPhone连接到xCode的电缆时,如果我断开iPhone并通过蜂窝接收推送通知,代码停在[self.downloadTask resume];线而不调用URL。

处理这些操作的类是NSURLSessionDataDelegate,NSURLSessionDownloadDelegate,NSURLSessionTaskDelegate,因此实现:

    - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler

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

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location

我试图在UILocalNotification之后插入一个带有[self.downloadTask resume](现在'现在')的调试行,但在5分钟后被调用并且说self.downloadTask.state被“暂停”

是什么导致这种奇怪的行为?

ios background nsurlsession cellular-network nsurlsessiondownloadtask
4个回答
8
投票

NSURLSessionConfiguration类参考的文档在这里:

https://developer.apple.com/Library/ios/documentation/Foundation/Reference/NSURLSessionConfiguration_class/Reference/Reference.html#//apple_ref/occ/instp/NSURLSessionConfiguration/discretionary

说:对于自由选择的财产:

讨论

设置此标志后,插入电源和Wi-Fi时更有可能发生传输。默认情况下,此值为false。

仅当最初通过调用backgroundSessionConfiguration:方法构造会话的配置对象时才使用此属性,并且仅在应用程序位于前台时启动的任务中使用此属性。如果在应用程序处于后台时启动任务,则无论此属性的实际值如何,该任务都将被视为自由裁量。对于基于其他配置创建的会话,将忽略此属性。

这似乎意味着如果在后台启动下载,操作系统始终可以自行决定是否以及何时继续下载。在完成这些任务之前,操作系统似乎总是在等待wifi连接。

我的经验支持这个猜想。我发现当设备处于蜂窝状态时,我可以发送几个下载通知。他们仍然卡住了。当我将设备切换到wifi时,它们都会通过。


1
投票

我遇到了同样的问题,最后我确定了

configuration.discretionary = NO;

一切正常,对于backgroundConfigurationdiscretionary = YES默认,似乎任务开始与WIFI和电池两者。希望有帮助


0
投票

你在做什么

  • (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void(^)(UIBackgroundFetchResult))completionHandler {}

您是否在下载完成之前立即调用completionHandler?我相信这样做不会影响Wifi模式或连接到Xcode时的操作。但不知何故,当在Cellular的背景下,它会使下载停止,直到你去Wifi。


0
投票

唯一真正的解决方法是在应用程序在后台并使用CF套接字时转储NSURLSession。如果我使用CFStreamCreatePairWithSocketToHost打开CFStream,我可以在应用程序处于后台时通过蜂窝成功执行HTTP请求

#import "Communicator.h"

@implementation Communicator {
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;

    NSInputStream *inputStream;
    NSOutputStream *outputStream;
    CompletionBlock _complete;
}

- (void)setupWithCallBack:(CompletionBlock) completionBlock {
    _complete = completionBlock;
    NSURL *url = [NSURL URLWithString:_host];

    //NSLog(@"Setting up connection to %@ : %i", [url absoluteString], _port);

    CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)[url host], _port, &readStream, &writeStream);

    if(!CFWriteStreamOpen(writeStream)) {
        NSLog(@"Error, writeStream not open");

        return;
    }
    [self open]; 

    //NSLog(@"Status of outputStream: %lu", (unsigned long)[outputStream streamStatus]);

    return;
}

- (void)open {
    //NSLog(@"Opening streams.");

    inputStream = (__bridge NSInputStream *)readStream;
    outputStream = (__bridge NSOutputStream *)writeStream;

    [inputStream setDelegate:self];
    [outputStream setDelegate:self];

    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

    [inputStream open];
    [outputStream open];
}

- (void)close {
    //NSLog(@"Closing streams.");

    [inputStream close];
    [outputStream close];

    [inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

    [inputStream setDelegate:nil];
    [outputStream setDelegate:nil];

    inputStream = nil;
    outputStream = nil;
}

- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)event {
    //NSLog(@"Stream triggered.");

    switch(event) {
        case NSStreamEventHasSpaceAvailable: {
            if(stream == outputStream) {
                if (_complete) {
                    CompletionBlock copyComplete = [_complete copy];
                    _complete = nil;
                    copyComplete();
                }
            }
            break;
        }
        case NSStreamEventHasBytesAvailable: {
            if(stream == inputStream) {
                //NSLog(@"inputStream is ready.");

                uint8_t buf[1024];
                NSInteger len = 0;

                len = [inputStream read:buf maxLength:1024];

                if(len > 0) {
                    NSMutableData* data=[[NSMutableData alloc] initWithLength:0];

                    [data appendBytes: (const void *)buf length:len];

                    NSString *s = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];

                    [self readIn:s];

                }
            } 
            break;
        }
        default: {
            //NSLog(@"Stream is sending an Event: %lu", (unsigned long)event);

            break;
        }
    }
}

- (void)readIn:(NSString *)s {
    //NSLog(@"reading : %@",s);
}

- (void)writeOut:(NSString *)s{
    uint8_t *buf = (uint8_t *)[s UTF8String];

    [outputStream write:buf maxLength:strlen((char *)buf)];

    NSLog(@"Writing out the following:");
    NSLog(@"%@", s);
}

@end
© www.soinside.com 2019 - 2024. All rights reserved.