我得到一個VoIP iOS應用程序使用長輪詢機制來維護其服務連接並接收事件(呼叫)。這意味着,NSURLConnection
正在等待幾分鐘,但會在事件發生後立即返回。由於VoIP標誌,即使應用程序處於後臺模式,也可以設置保持活動處理程序並接收更新。iOS:NSURLConnection回調嚴重延遲或根本沒有被解僱
但是,這大多數時間工作但不可靠。有時,即使在請求超時(到達NSURLRequest
的timeoutInterval
)之後,NSURLConnection
回調也會嚴重延遲或根本沒有被解僱。
從日誌中的例子來闡明:
- 該應用程序在後臺模式下
NSURLConnection
#1(長輪詢)(由系統在引導時啓動)運行被啓動,並且返回後1一些新的數據NSURLConnection
#2(長輪詢)分鐘開始和15後的回報議事錄(服務器端最大),沒有任何新的數據- (...)
NSURLConnection
#99(長輪詢)已啓動,但不會返回 - 即使在timeoutInterval
已過期(16分鐘)之後也沒有。- 有時候,保持活動處理程序被調用,bot沒有任何反應。該
backgroundTimeRemaining
財產得到了不切實際的高價值(179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0
而不是最大180.0
)。 - 1小時後,用戶打開應用程序。該應用程序能夠執行各種
NSURLRequest
S和receivce反應 - 一些secounds後,用戶關閉應用程序
- 10多分鐘後,
NSURLConnection
#99回調didFailWithError
得到了與超時錯誤(-1001)解僱。這個請求的執行時間超過了一個小時,甚至很難將timeoutInterval
限制在16分鐘以內,還有幾個其他的請求在稍後啓動,但是之前完成。
從我的角度來看,這似乎是iOS的一個非常奇怪的行爲。爲什麼iOS應用程序需要後臺執行時間並調用保持活動處理程序,但是沒有及時正確地觸發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
,並且從未遇到過這種問題。