【发布时间】:2019-05-23 17:41:23
【问题描述】:
我正在尝试在 iOS 应用中实现后台获取。在官方documentation中声明:
“您不必使用后台会话来执行所有后台网络活动……。声明适当后台模式的应用程序可以使用默认 URL 会话和数据任务,就像它们在前景。”
此外,在这个 WWDC 2014 video 51:50 之间的最佳实践中陈述了:
“我们认为有些人过去犯的一个常见错误是假设在后台运行以处理诸如后台获取更新之类的事情时... ...他们被要求使用后台会话. 现在,使用后台上传或下载... ...效果很好,但特别是对于大型下载。当您运行后台获取更新时...您有大约 30 到 60 秒的时间在后台运行" [在应用程序暂停之前] "如果您有相同的小型网络任务可以在这段时间内完成,那么在进程内或默认的 NSURLSession 中执行此操作是完全可以的......" p>
我正处于一个小型网络任务的情况:我想与我们服务器上的一个 rest api 通信,发布相同的数据并取回数据响应。我正在创建一个默认会话并执行一个或两个任务,使用调度组同步它们,如下例所示:
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(@"AppDelegate application:performFetchWithCompletionHandler:");
BOOL thereIsALoggedInUser = [self thereIsALoggedInUser];
if (!(thereIsALoggedInUser && [application isProtectedDataAvailable])){
completionHandler(UIBackgroundFetchResultNoData);
NSLog(@"completionHandler(UIBackgroundFetchResultNoData): thereIsALoggedInUser: %@, isProtectedDataAvailable: %@",
thereIsALoggedInUser ? @"YES" : @"NO",
[application isProtectedDataAvailable] ? @"YES" : @"NO");
return;
}
NSArray<NSString*> *objectsIWantToSend = [self getObjectsIWantToSend];
if (!objectsIWantToSend) {
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalNever];
completionHandler(UIBackgroundFetchResultNoData);
NSLog(@"No post data");
} else {
[self sendToServer:objectsIWantToSend usingCompletionHandler:completionHandler];
}
}
-(void) sendToServer:(NSArray<NSString*> *)objectsArray usingCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(@"AppDelegate - sendToServer:usingCompletionHandler:");
__block BOOL thereWasAnError = NO;
__block BOOL thereWasAnUnsuccessfullHttpStatusCode = NO;
dispatch_group_t sendTransactionsGroup = dispatch_group_create();
NSURLSession *postSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
for (NSString *objectToPost in objectsArray) {
dispatch_group_enter(sendTransactionsGroup);
NSMutableURLRequest *request = [NSMutableURLRequest new];
[request setURL:[NSURL URLWithString:@"restApiUrl"]];
[request setHTTPMethod:@"POST"];
request = [Util setStandardContenttypeAuthorizationAndAcceptlanguageOnRequest:request];
[request setHTTPBody:[NSJSONSerialization dataWithJSONObject:@{ @"appData": objectToPost } options:kNilOptions error:nil]];
NSURLSessionDataTask *dataTask = [postSession dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
NSInteger statusCode = -1;
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
statusCode = [(NSHTTPURLResponse *)response statusCode];
}
if (error) {
NSLog(@"Error");
thereWasAnError = YES;
} else if (statusCode != 201) {
NSLog(@"Error: http status code: %d", httpResponse.statusCode);
thereWasAnUnsuccessfullHttpStatusCode = YES;
} else {
[self parseAndStoreData:data];
}
dispatch_group_leave(sendTransactionsGroup);
}];
[dataTask resume];
}
dispatch_group_notify(sendTransactionsGroup, dispatch_get_main_queue(),^{
if (thereWasAnError) {
completionHandler(UIBackgroundFetchResultFailed);
} else if (thereWasAnUnsuccessfullHttpStatusCode) {
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalNever];
completionHandler(UIBackgroundFetchResultNoData);
} else {
completionHandler(UIBackgroundFetchResultNewData);
}
});
}
在我的应用程序中:didFinishLaunchingWithOptions: 方法我将 BackgroundFetchInterval 设置为 MinimumInterval 使用
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
我没有强制退出设备上的应用程序,因为我知道在这种情况下系统不会调用 AppDelegate application:performFetchWithCompletionHandler: 方法,直到用户重新启动应用程序
我正在使用几个测试用例,其中包含锁定/解锁、充电/不充电的几种设备组合,所有这些都在 wifi 网络下,因为它们没有模拟人生
当我使用 Xcode -> Debug -> Simulate background fetch 或使用构建方案运行上述类型的代码时,选择了“由于后台获取事件而启动”选项,一切顺利:服务器收到我的发布请求,应用程序正确地返回了服务器响应,并且 dispatch_group_notify 块被执行。通过实际设备测试代码我没有得到任何结果,甚至没有对服务器的调用
【问题讨论】:
-
好的,所以你在你的应用程序委托中到处都在记录,你现在正在控制台中观看这个?你看到它叫
applicationWillResignActive,但从来没有performFetchWithCompletionHandler? (如果是这样,那么sendToServer是无关紧要的。)然后我建议两件事: 1. 完全卸载并重新安装应用程序(如果您还没有)以重置后台获取。 2. 创建新的MCVE,它执行后台获取(带有一些NSURLSession任务)并确保您的应用程序中没有其他东西干扰进程。 -
谢谢,MCVE 方法是可行的方法:我使用 Postman Echo 网站提供的端点在专用应用程序中测试了我的实现,一切顺利:调用了 application:performFetch 方法,并且 dispatch_group 的使用完全没有给我带来麻烦。我还没有设法诊断出问题,但我可以确认数据任务完成了它的工作。我发现应用源中有一个重复的 Info.plist 文件,并且其中一个副本没有 UIBackgroundModes-fetch 键值对,我现在正在调查这个问题
标签: ios nsurlsessiondatatask background-fetch