13

当应用程序收到推送通知时,我已启用具有远程通知任务的后台模式以在后台下载一个小文件 (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 或蜂窝网络连接但 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

我尝试在 5 分钟后插入带有UILocalNotification(显示为“现在”)的调试行,[self.downloadTask resume]但在 5 分钟后被调用,并说self.downloadTask.state“已暂停”

这种奇怪的行为是什么引起的?

4

4 回答 4

9

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 时,它们都通过了。

于 2014-09-03T20:27:55.117 回答
3

我遇到了同样的问题,最后我设置了

configuration.discretionary = NO;

一切正常,因为backgroundConfiguration默认 discretionary = YES情况下,似乎任务开始WIFI与电池有关。希望有帮助

于 2016-03-13T06:28:40.377 回答
0

你在做什么

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

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

于 2014-09-03T00:30:54.560 回答
0

解决此问题的唯一真正方法是在应用程序处于后台时转储 NSURLSession 并使用 CF 套接字。如果我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
于 2016-07-15T09:33:02.923 回答