我使用长轮询机制获得了一个VoIP iOS应用程序来维护其服务连接并接收事件(调用)。这意味着,NSURLConnection
等待几分钟,但会在事件发生后立即返回。由于VoIP标志,即使应用程序处于后台模式,也可以设置保持活动处理程序并接收更新。
但是,大部分时间但不可靠。有时,即使请求超时(NSURLConnection
已达到timeoutInterval
),NSURLRequest
回调也会严重延迟或根本不会被触发。
日志中的一个例子澄清:
NSURLConnection
#1(长轮询)启动并在1分钟后返回一些新数据NSURLConnection
#2(长轮询)启动并在15分钟后(服务器端最大值)返回,没有任何新数据NSURLConnection
#99(长轮询)已启动,但未返回 - 甚至在timeoutInterval
到期后(16分钟)backgroundTimeRemaining
属性获得了不切实际的高值(179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0
,而不是最大值180.0
)。NSURLRequest
和接收回复NSURLConnection
#99回调didFailWithError
因超时错误(-1001)而被解雇。此请求的超时时间超过一小时,即使timeoutInterval
被限制为16分钟,还有其他几个请求在稍后启动但稍早完成。从我的角度来看,这似乎是iOS的一种非常奇怪的行为。 iOS为什么要给应用程序提供后台执行时间并调用keep alive处理程序,但是没有及时正确触发NSURLConnection回调?
保持活着的处理程序:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{
NSLog(@"########### Started Keep-Alive Handler ###########");
[self startBackgroundHandler:YES timeout:30];
NSLog(@"########### Completed Keep-Alive Handler ###########");
}];
[self startBackgroundHandler:NO timeout:60];
}
-(void)startBackgroundHandler:(BOOL)force timeout:(int)timeout {
UIApplicationState currentAppState = [[UIApplication sharedApplication] applicationState];
BOOL appIsBackground = currentAppState == UIApplicationStateBackground;
if(appIsBackground || force) {
int localThreadId = ++_currentBackgroundThreadId;
__block UIBackgroundTaskIdentifier bgTask;
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
NSLog(@"Cleaning up [Background Thread %d] ...", localThreadId);
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
NSLog(@"startBackgroundHandler with [Background Thread %d] appIsBackground=%d force=%d", localThreadId, appIsBackground, force);
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if(_currentBackgroundThreadId == localThreadId) {
NSLog(@"[Background Thread %d] Background time left: %0.1f", localThreadId, [UIApplication
sharedApplication].backgroundTimeRemaining);
sleep(timeout);
}
NSLog(@"[Background Thread %d] Will exit...", localThreadId);
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
} else {
NSLog(@"Ignored startBackgroundHandler - appIsBackground=%d force=%d", appIsBackground, force);
}
}
所有NSURLConnections都有一个runloop - 它们按如下方式启动:
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:mrequest delegate:self startImmediately:NO];
if(connection) {
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[connection start];
} else {
// error handling...
}
PS:
在应用程序的早期版本中,使用data fetch
后台模式而不是voip
,并且从未遇到过这种问题。
答案 0 :(得分:1)
似乎iOS不愿意在你处于后台时给你主线程上的CPU时间 - 即使你有VoIP标志。因此,您应该在单独的线程中安排您的请求,并使用CFRunLoopRun()
使其在后台运行。此外,您必须使用runloop上的runMode:beforeDate:
触发执行适当的模式。
但真正的问题是,如果请求经常超时,iOS将停止提供CPU时间 - 你真的需要接收任何东西才能获得CPU时间。因此,让WebServer及时回复非常重要。